Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)

3,501 views

Published on

https://www.facebook.com/eeRhapsody
MCS-51 實驗 - 使用 IAR (1)

Published in: Engineering
  • Login to see the comments

[嵌入式系統] MCS-51 實驗 - 使用 IAR (1)

  1. 1. Chien-Jung Li Sept. 2013 MCS-51 基礎實習 使用IAR Embedded Workbench (I)
  2. 2. 開發板認識 2
  3. 3. 燒錄程式的安裝 (I) 1) PC安裝PL2303_Prolific_DriverInstaller_v1210驅動程式。 2) 將燒錄程式「STC-ISP-V488_燒錄程式_免安裝」解壓 縮 到 D 槽 ( 或 C 槽 也 可 ) 。 解 完 後 將 目 錄 裡 的 STC_ISP_V488.exe建立桌面捷徑。 3
  4. 4. 燒錄程式的安裝 (II) 3) 安裝AppLocale: papplc.msi (要先改相容性)。裝完後, 再點擊「簡體中文執行_右鍵選單註冊.reg」。 4
  5. 5. 燒錄程式的安裝 (III) 4) 點選STC_ISP_488程式,從右鍵選單中選擇「以簡體中 文執行」。(在64位元win7無法開啟程式,請見下頁) 5
  6. 6. 64位元win7系統使用STC-ISP程式  在64位元系統要執行ISP時,可能出現以下錯誤:  解法: 1) 到 D:STC-ISP-V488 目 錄 下 將 MSSTDFMT.dll 、 comdlg32.ocx 、 mscomctl.ocx 、 mscomm32.ocx 這 四 支 檔 案 拷 貝 至 C:WindowsSysWOW64目錄下。(若檔案已存在,不要覆蓋) 2) 在C:WindowsSysWOW64目錄下找到cmd.exe,以管理員執行。 接著於終端輸入 regsvr32 mscomm32.ocx regsvr32 comdlg32.ocx 註 冊 完 再 執 行 ISP 程 式 試 試 看 , 如 果 還 不 能 執 行 , 再 註 冊 MSSTDFMT.dll與mscomctl.ocx這兩支檔案。 6
  7. 7. 燒錄程式的安裝 (IV) 5) 接上USB (在裝置管理員看一下連接埠號碼) 6) 執行ISP燒錄程式 7) 選擇一支測試映像檔燒錄看看(作法見下頁) 7
  8. 8. 燒錄映像檔 8
  9. 9. 安裝8051嵌入式開發環境  目前大多數的MCS-51開發都以Kiel C這套軟體為主  為了能跟以後要開發的SoC晶片銜接,我們改用IAR 這套IDE來進行開發。 9
  10. 10. 使用IAR – 開啟新專案 10
  11. 11. 環境設定 (I) – 選擇裝置 11
  12. 12. 環境設定 (II) – 設定Linker 12
  13. 13. 環境設定 (III) – 其它 (可不用設定) 13
  14. 14. 第一支程式 (I) 14
  15. 15. 第一支程式 (II) #include<ioAT89C52.h> void main() { unsigned int a = 0; while(1) { a = 50000; while(a--); a = 50000; while(a--); a = 50000; P1 &= ~(1 << 2); while(a--); a = 50000; while(a--); a = 50000; P1 &= ~(1 << 0); P1 &= ~(1 << 5); P1 &= ~(1 << 6); while(a--); P1 |= (1 << 6); } } 15
  16. 16. 第一支程式 (III) 16
  17. 17. 認識MCU的Header File
  18. 18. MCS-51 AT89C52介紹  Compatible with MCS-51™ Products  8K Bytes Flash Memory  Fully Static Operation: 0 Hz to 24 MHz  256 x 8-bit Internal RAM  32 Programmable I/O Lines  Three 16-bit Timer/Counters  8 Interrupt Sources  Programmable Serial Channel  Low-power Idle and Power-down Modes 18
  19. 19. 特殊暫存器(SFR) – 透過它們控制MCU 19
  20. 20. Header File /*************************************************************************** * - ioAT89C52.h - * * Special header for the Atmel AT89C52 Microcontroller. ***************************************************************************/ #ifndef IOAT89C52_H #define IOAT89C52_H #define __AT89C52__ #ifdef __IAR_SYSTEMS_ICC__ #ifndef _SYSTEM_BUILD #pragma system_include #endif #pragma language=extended /*------------------------------------------------------------------------- * Power and clock control registers *-------------------------------------------------------------------------*/ __sfr __no_init volatile union { unsigned char PCON; /* Power Control */ struct /* Power Control */ { unsigned char IDL : 1; unsigned char PD : 1; unsigned char GF0 : 1; unsigned char GF1 : 1; unsigned char : 1; unsigned char : 1; unsigned char : 1; unsigned char SMOD : 1; } PCON_bit; } @ 0x87; 20
  21. 21. __sfr __no_init volatile unsigned char TH0 @ 0x8C; /* Timer/Counter 0 High Byte */ __sfr __no_init volatile union { unsigned char TCON; /* Timer/Counter Control */ struct /* Timer/Counter Control */ { unsigned char IT0 : 1; unsigned char IE0 : 1; unsigned char IT1 : 1; unsigned char IE1 : 1; unsigned char TR0 : 1; unsigned char TF0 : 1; unsigned char TR1 : 1; unsigned char TF1 : 1; } TCON_bit; } @ 0x88; __sfr __no_init volatile unsigned char TH2 @ 0xCD; /* Timer/Counter 2 High Byte */ __sfr __no_init volatile unsigned char TH1 @ 0x8D; /* Timer/Counter 1 High Byte */ __sfr __no_init volatile union { unsigned char TMOD; /* Timer/Counter Mode Control */ struct /* Timer/Counter Mode Control */ { unsigned char M00 : 1; unsigned char M10 : 1; unsigned char C_T0 : 1; unsigned char GATE0 : 1; unsigned char M01 : 1; unsigned char M11 : 1; unsigned char C_T1 : 1; unsigned char GATE1 : 1; } TMOD_bit; } @ 0x89; 21
  22. 22. /*------------------------------------------------------------------------- * I/O port registers *-------------------------------------------------------------------------*/ __sfr __no_init volatile union { unsigned char P1; /* Port 1 */ struct /* Port 1 */ { unsigned char P1_0 : 1; unsigned char P1_1 : 1; unsigned char P1_2 : 1; unsigned char P1_3 : 1; unsigned char P1_4 : 1; unsigned char P1_5 : 1; unsigned char P1_6 : 1; unsigned char P1_7 : 1; } P1_bit; } @ 0x90; __sfr __no_init volatile union { unsigned char P0; /* Port 0 */ struct /* Port 0 */ { unsigned char P0_0 : 1; unsigned char P0_1 : 1; unsigned char P0_2 : 1; unsigned char P0_3 : 1; unsigned char P0_4 : 1; unsigned char P0_5 : 1; unsigned char P0_6 : 1; unsigned char P0_7 : 1; } P0_bit; } @ 0x80; /*--------------------------------------------- * Interrupt system registers *---------------------------------------------*/ __sfr __no_init volatile union { unsigned char IE; /* Interrupt Enable Control */ struct /* Interrupt Enable Control */ { unsigned char EX0 : 1; unsigned char ET0 : 1; unsigned char EX1 : 1; unsigned char ET1 : 1; unsigned char ES : 1; unsigned char ET2 : 1; unsigned char : 1; unsigned char EA : 1; } IE_bit; } @ 0xA8; 22
  23. 23. 小程式 #include <ioAT89C52.h> void main(){ // 主程式一定叫main() while(1){ // 嵌入式系統會持續run下去 P1_bit.P1_0 = 0; // 設定port1的第0埠 // 假設該腳是Active Low } } 23
  24. 24. Data Types (IAR) 24
  25. 25. 自己定義資料型別關鍵字 25 typedef signed char int8; typedef unsigned char uint8; typedef signed short int16; typedef unsigned short uint16; typedef signed long int32; typedef unsigned long uint32; typedef unsigned char bool;
  26. 26. Lab1:以點亮LED學習輸出
  27. 27. 範例實習1  請打開lab1_Output_LED  練習1: 學習依照header filer指定SFR來使用輸出  練習2: 使用#define來訂別名  練習3, 4: 使用「位元組」操作LED  練習5: 學習移位運算子  練習6: 使用巨集  練習7, 8: 延遲為ms等級的函數 27
  28. 28. 練習1:SFR的使用 28 /************************************************************* Filename: exercise1-01.c Description: 學習如何依照header filer對I/O SFR的定義來使用輸出 **************************************************************/ #include<ioAT89C52.h> // 這個header定義了SFR的"名稱"與記憶體位址的對應 // 以後我們就可以直接對SFR的"名稱變數"操作,而不用對"記憶體位址"的內容作操作 void main() { while(1) { P1_bit.P1_0 = 0;// AT89C52開發板輸出 // 是active low, // 輸出為0時, LED點亮 } } // 開發板上的八顆LED {D0, D1, ..., D7}, // 分別對應到 {P1_0, P1_1, ..., P1_7} // 練習1: 點亮D1 // 練習2: 點亮D0, D1, D7 __sfr __no_init volatile union { unsigned char P1; /* Port 1 */ struct /* Port 1 */ { unsigned char P1_0 : 1; unsigned char P1_1 : 1; unsigned char P1_2 : 1; unsigned char P1_3 : 1; unsigned char P1_4 : 1; unsigned char P1_5 : 1; unsigned char P1_6 : 1; unsigned char P1_7 : 1; } P1_bit; } @ 0x90;
  29. 29. 練習2:預編譯語法#define的使用 29 /********************************************************************** Filename: exercise1-02.c Description: 使用#define定義port的"別名",用"別名"有時候看起來會更容易理解 ***********************************************************************/ #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 #define LED1 P1_bit.P1_1 #define LED2 P1_bit.P1_2 #define LED3 P1_bit.P1_3 #define LED4 P1_bit.P1_4 #define LED5 P1_bit.P1_5 #define LED6 P1_bit.P1_6 #define LED7 P1_bit.P1_7 void main() { while(1) { LED0 = 0; } } // 練習1: 使用別名點亮D1 // 練習2: 使用別名點亮D0, D1, D7 #define LED0 P1_bit.P1_0 #define LED1 P1_bit.P1_1
  30. 30. 練習3:使用位元組設定操作LED 30 /************************************************************ Filename: exercise1-03.c Description: 對位元組"P1"操作來點亮LED0,而不是用單一位元的操作 *************************************************************/ #include<ioAT89C52.h> void main() { while(1) { P1 = 0xFE; // 1111,1110 } } // 開發板上的八顆LED {D0, D1, ..., D7}, // 分別對應到 {P1_0, P1_1, ..., P1_7} // // 練習1: 使用 #define 將P1的別名設定為 LED, // 然後對"LED"別名進行操作同時點亮 // LED0, LED2, LED4 P1_bit.P1_0 = 0; // 位元操作 P1 = 0xFE; // 位元組操作
  31. 31. 練習4:使用位元組設定操作LED 31 /******************************************************************************* Filename: exercise1-04.c Description: 操作位元組"P1"點亮D2,延時一段時間後,"再"點亮D0, D5, D6 (D2不可熄滅) *******************************************************************************/ #include<ioAT89C52.h> void main() { unsigned int a = 0; // 在IAR/Kiel中, int的型別是16位元, unsigned int範圍為0~65535 while(1) { a = 50000; P1 = 0xFB; // 1111,1011 點亮D2 while(a--); a = 50000; P1 = 0x9A; // 1001,1010 點亮D0, D5, D6, 但D2也維持點亮 while(a--); while(1); // 將此行註解掉, LED會開始閃爍 // while(1)不可亂用, 因程式會在此卡死, 這裡只是demo } } // 練習1: 對"位元組"使用"位元操作"運算, 將第16行更改為 // P1 &= ~(0x61); // 0110,0001 // 想想看, 這是甚麼原理? // 練習2: 點亮D0, D2, D5, D6之後,延遲一段時間,熄滅D6。將18行改為 // a = 50000; // P1 |= 0x40; // 0100,0000 // while(a--); // 想想看, 這是甚麼原理? // 當我們要對"位元組"內的某個、或某幾個bits做設定,但又不影響其他bits原有的狀態時, // 位元操作運算 &= 與 |= 是非常實用而且常見的運算手法喔! P1 &= ~(0x61) // 位元操作, 設為0 P1 |= 0x40 // 位元操作, 設為1
  32. 32. /**************************************************************************************** Filename: exercise1-05.c Description: 學會使用移位操作,設定"P1"點亮D2,延時一段時間後,"再"點亮D0, D5, D6 (D2不可熄滅) 再延遲一段時間後,關閉熄滅D6 *****************************************************************************************/ #include<ioAT89C52.h> void main() { unsigned int a = 0; while(1) { a = 50000; while(a--); a = 50000; while(a--); a = 50000; P1 &= ~(1 << 2); // 將1左移2位再取~ 得到 1111,1011 點亮D2 while(a--); a = 50000; while(a--); a = 50000; P1 &= ~(1 << 0); // 0000,0001 -> 1111,1110 點亮D0 P1 &= ~(1 << 5); // 0010,0000 -> 1101,1111 點亮D5 P1 &= ~(1 << 6); // 0100,0000 -> 1011,1111 點亮D6 while(a--); P1 |= (1 << 6); // 0100,0000 熄滅D6 } } 練習5:學習移位運算子 32 P1 &= ~(1<<2); // 位元操作, 設為0 P1 |= (1<<2); // 位元操作, 設為1
  33. 33. /************************************************************************************************** Filename: exercise1-06.c Description: 學會使用巨集(macro)以簡化程式碼的寫法, 重新改寫exercise1-05.c **************************************************************************************************/ #include<ioAT89C52.h> #define BV(n) (1 << (n)) // 這是向左移位操作的macro void main() { unsigned int a = 0; while(1) { a = 50000; while(a--); a = 50000; while(a--); a = 50000; P1 &= ~BV(2); // 將1左移2位再取~ 得到 1111,1011 點亮D2 while(a--); a = 50000; while(a--); a = 50000; P1 &= ~BV(0); // 0000,0001 -> 1111,1110 點亮D0 P1 &= ~BV(5); // 0010,0000 -> 1101,1111 點亮D5 P1 &= ~BV(6); // 0100,0000 -> 1011,1111 點亮D6 while(a--); P1 |= BV(6); // 0100,0000 熄滅D6 } } 練習6:使用巨集(Macro) 33 #define BV(n) (1 << (n))
  34. 34. /************************************************************************* Filename: exercise1-07.c Description: 將延時放的更長 ***************************************************************************/ #include<ioAT89C52.h> #define BV(n) (1 << (n)) // 這是向左移位操作的macro typedef signed char int8; // 以後8位元的有號數就可以用int8做宣告 typedef unsigned char uint8; // 以後8位元的無號數就可以用uint8做宣告 void delay(uint8); // delay函數的宣告 void main() { while(1) { P1 &= ~BV(2); // 將1左移2位再取~ 得到 1111,1011 點亮D2 delay(255); delay(255); P1 &= ~BV(0); // 0000,0001 -> 1111,1110 點亮D0 P1 &= ~BV(5); // 0010,0000 -> 1101,1111 點亮D5 P1 &= ~BV(6); // 0100,0000 -> 1011,1111 點亮D6 delay(255); P1 |= BV(6); // 0100,0000 熄滅D6 delay(150); } } void delay(uint8 d) // delay函數的定義, 也就是這個函數要執行的動作 { uint8 x,y; for(x=d;x>0;x--) for(y=255;y>0;y--); } // 使用雙for迴圈來延長時間, 最高可延長 256*256 = 65536 (65535遞減至0) // 在這個練習裡, 你也學到函數的宣告與定義 // 練習: 試著改變延時的長度 練習7:延時函式 34 unsigned int a = 0; a = 50000; while(a--);
  35. 35. 練習8:毫秒(ms)等級的延時函式 35 /************************************************************************ Filename: exercise1-08.c Description: 1ms延遲函數, 將延時放的更長 *************************************************************************/ #include<ioAT89C52.h> #define BV(n) (1 << (n)) // 這是向左移位操作的macro typedef signed char int8; // 以後8位元的有號數就可以用int8做宣告 typedef unsigned char uint8; // 以後8位元的無號數就可以用uint8做宣告 void delayms(uint8); // delay函數的宣告 void main() { while(1) { P1 &= ~BV(0); // 點亮D0 delayms(255); delayms(245); P1 |= BV(0); // 熄滅D0 delayms(255); delayms(245); } } void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } }
  36. 36. 小錦囊 (I) 36 #define LED0 P1_bit.P1_0 P1 &= ~(1 << 0); // 0000,0001 -> 1111,1110 P1 |= (1 << 6); // 0100,0000 #define BV(n) (1 << (n)) // 這是向左移位操作的macro P1 &= ~BV(5); // 0010,0000 -> 1101,1111 P1 |= BV(6); // 0100,0000 typedef signed char int8; typedef unsigned char uint8;
  37. 37. 小錦囊 (II) 37 void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } }
  38. 38. 驅動程式的概念 38  把「對硬體的操作」包裝成函式,即為該硬體裝置 的驅動程式(這是最簡單的說法)。  一般而言,嵌入式系統驅動程式的介面都是以模仿 UNIX系統呼叫設備的API為基礎,介面十分直接: open():開啟裝置,嵌入式系統有時也用init()取代 close():關閉裝置 read():自裝置讀取資料 write():傳送資料到裝置 ioctl():I/O的控制以及處理介面其他未涵蓋的部分
  39. 39. LED0的驅動程式(I) 39  為方便說明,我們僅以單一顆LED0的閃爍為例: /********************************************************************* Filename: exercise1-09.c Description: 以函式包裝對LED0的操作(最簡單的驅動程式) *********************************************************************/ #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 typedef unsigned char uint8; /* Declare Function Prototype */ void drv_LED0_Init(void); void drv_LED0_Toggle(void); void delayms(uint8); void main(void) { drv_LED0_Init(); while(1) { drv_LED0_Toggle(); // Change LED state delayms(250); // Delay for 250 ms delayms(250); // Delay for 250 ms, 1 Hz Flash = 0.5s On + 0.5s OFF } }
  40. 40. LED0的驅動程式(II) 40 /*********************************************************************** * @fn drv_LED0_Init * @brief Initialize LED0 State * @param None * @return None **********************************************************************/ void drv_LED0_Init(void) { LED0 = 1; // 初始時LED0熄滅 } /*********************************************************************** * @fn drv_LED0_Toggle * @brief Changes LED0 State * @param None * @return None **********************************************************************/ void drv_LED0_Toggle(void) { // Change the LED from OFF to ON (or vice versa) LED0 = ~LED0; }
  41. 41. LED0的驅動程式(III) 41 /********************************************************************** * @fn delayms * @brief delay with ms * @param time = 0 ~ 255, the maximum delay is 255 ms * @return None *********************************************************************/ void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } } /********************************************************************* **********************************************************************/
  42. 42. /************************************ * @fn drv_LED0_Off * @brief Turn Off LED0 * @param None * @return None ************************************/ void drv_LED0_Off(void) { LED0 = 1; } 用On/Off函式並使用全域變數(I) 42  將exercise1-09.c另存為exercise1-10.c,並加入: uint8 gLED0_state; // 'g' notice us it's a global var. void drv_LED0_Init(void); void drv_LED0_On(void); void drv_LED0_Off(void); void drv_LED0_Toggle(void); void delayms(uint8); /************************************ * @fn drv_LED0_On * @brief Turn On LED0 * @param None * @return None ************************************/ void drv_LED0_On(void) { LED0 = 0; }
  43. 43. 用On/Off函式並使用全域變數(II) 43 /*************************************************************** * @fn drv_LED0_Toggle * @brief Changes LED0 State * @param None * @return None **************************************************************/ void drv_LED0_Toggle(void) { // Change the LED from OFF to ON (or vice versa) if (gLED0_state == 1) { gLED0_state = 0; drv_LED0_Off(); } else { gLED0_state = 1; drv_LED0_On(); } }
  44. 44. Lab2:按壓按鍵點亮LED學習輸入
  45. 45. 鍵盤分類 45
  46. 46. 範例實習2  請打開lab2_Input_Button  練習1: 使用輸入 (持續按下時LED點亮)  練習2: 使用輸入 (按一下button即可開關LED)  練習3: 簡易軟體debounce  練習4: 以函式包裝檢測按鍵是否被按下的功能 46
  47. 47. 練習1:持續按下時LED點亮 47 /********************************************************************************** Filename: exercise2-01.c Description: 使用button (這個程式還未使用中斷) ***********************************************************************************/ #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 #define BTN1 P3_bit.P3_2 void main() { BTN1 = 1; // I/O要當輸入時,要先寫入1 while(1) { if (BTN1 == 0){ LED0 = 0; }else{ LED0 = 1; } } } // 程式效果: 按鍵按下(持續按下), LED0維持點亮, 只要一放開就熄滅. // 開發板上的4個獨立按鍵 {INT0, INT1, T0, T1}, 分別對應到 {P3_2, P3_3, P3_4, P3_5} // 練習1: 試著使用其他按鍵 // 練習2: 試著做出一個 #define LED0 P1_bit.P1_0 #define BTN1 P3_bit.P3_2
  48. 48. 練習2:按一下按鈕就改變LED 48 /**************************************************************************************** Filename: exercise2-02.c Description: 使用button (這個程式還未使用中斷) ****************************************************************************************/ #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 #define BTN1 P3_bit.P3_2 // 這裡使用全域變數, 當旗標使用, 初始值為0 volatile unsigned char btn_flag = 0; void main() { BTN1 = 1; // I/O要當輸入時,要先寫入1 while(1) { if (BTN1 == 0) btn_flag = ~btn_flag; // 當btn按下, 所有位元反向 if (btn_flag != 0) LED0 = 0; // 若btn_flag不為0, 就點亮LED0 else LED0 = 1; // 反之btn_flag若為0, 就熄滅LED0 } } // 燒錄完後,試試看, 有沒有發現甚麼很詭異的現象? volatile unsigned char btn_flag = 0;
  49. 49. Bouncing +5V +5V A B 49
  50. 50. 小錦囊:軟體Debounce小程式 50 if (BTN1 == 0) { delayms(50); if (BTN1 == 0){ while(BTN1 == 0); btn_flag = ~btn_flag; } }
  51. 51. 練習3:去抖動 51 /*********************************************************************************** Filename: exercise2-03.c Description: 使用button / Simple Debounce (這個程式還未使用中斷) ************************************************************************************/ #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 #define BTN1 P3_bit.P3_2 typedef unsigned char uint8; volatile unsigned char btn_flag = 0; void delayms(uint8); void main() { BTN1 = 1; // I/O要當輸入時,要先寫入1 while(1) { if (BTN1 == 0) { delayms(50); if (BTN1 == 0){ // 這裡好像少了甚麼 btn_flag = ~btn_flag; } } if ((btn_flag & 0x01) == 1) LED0 = 0; else LED0 = 1; } } void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } }
  52. 52. 練習4:以函式包裝對硬體的操作 52 /********************************************************************** Filename: exercise2-04.c Description: 使用button / Simple Debounce (這個程式還未使用中斷) ***********************************************************************/ #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 #define BTN1 P3_bit.P3_2 #define BTN_NOT_PRESSED 0 #define BTN_PRESSED 1 #define LED_ON 0 #define LED_OFF 1 typedef unsigned char uint8; volatile unsigned char btn_flag = 0; void BTN_Init(void); uint8 BTN_Get_Input(uint8 debounce_time); void delayms(uint8); void main() { BTN_Init(); while(1) { if( BTN_Get_Input(50) ){ btn_flag = ~btn_flag; } LED0 = (btn_flag?LED_ON:LED_OFF); } } void BTN_Init(void) { BTN1 = 1; // I/O要當輸入時,要先寫入1 } uint8 BTN_Get_Input(uint8 debounce_time) { uint8 BTN_val = BTN_NOT_PRESSED; if (BTN1 == 0) { delayms(debounce_time); if (BTN1 == 0) { while(BTN1==0); BTN_val = BTN_PRESSED; } } return BTN_val; // Return BTN value } void delayms(uint8 time) { 略 }
  53. 53. 動態掃描 Lab3:七段顯示器與行列式鍵盤
  54. 54. 符號 0 1 2 3 4 5 編碼 0x3F 0x06 0x5B 0x4F 0x66 0x6D 符號 6 7 8 9 A B 編碼 0x7D 0x07 0x7F 0x6F 0x77 0x7C 符號 C D E F 無顯示 編碼 0x39 0x5E 0x79 0x71 0x00 七段顯示器 54 0 1 2 3 4 5 6 7
  55. 55. 開發板上的配置 55 pos_set=1; P0 = 0xFE; pos_set=0; seg_set=1; P0=0x71; seg_set=0;
  56. 56. 靜態顯示與動態顯示 56
  57. 57. 範例實習3  請打開lab3_7seg_keypad  練習1:靜態顯示一位 (練習: 用按鍵改變顯示位)  練習2:靜態顯示多位,並隨時間流逝遞增符號 (練習:改用按鍵改變顯示符號)  練習3:使用動態掃描同時顯示多位7段顯示器  練習4:掃描式矩陣鍵盤 57
  58. 58. 練習1:點亮一顆七段顯示器 58 /********************************************************************* Filename: exercise3-01.c Description: 點亮一顆7段顯示器 *********************************************************************/ #include<ioAT89C52.h> #define seg_set P2_bit.P2_6 #define pos_set P2_bit.P2_7 #define SEG7LED P0 #define POS7LED P0 void main() { pos_set = 1; POS7LED = 0xFE; // 1111,1110 pos_set = 0; seg_set = 1; SEG7LED = 0x71; // 0111,0001 seg_set = 0; while(1); } // 練習: 按下按鍵, 依序點亮其他的7段顯示器
  59. 59. 練習2:點亮多顆顯示器(I) 59 /******************************************************************* Filename: exercise3-02.c Description: 依序顯示符號0, 1,2,3, ->, A, b, C, d, E, F ********************************************************************/ #include<ioAT89C52.h> #define seg_set P2_bit.P2_6 #define pos_set P2_bit.P2_7 #define SEG7LED P0 #define POS7LED P0 typedef unsigned char uint8; void DispScrClr(void); void DispShowPos(uint8 pos); void DispShowSym(uint8 sym_num, uint8 dp); void delayms(uint8 time); // 符號表 0,1,2,...,9, A, b, C, d, E, F uint8 symbols[]={ 0x3F,0x06,0x5B,0x4F, 0x66,0x6D,0x7D,0x07, 0x7F,0x6F,0x77,0x7C, 0x39,0x5E,0x79,0x71}; void main() { DispScrClr(); uint8 symbol_num; while(1) { DispShowPos(0xF0); // 1111, 0000 (pos0~3) for(symbol_num=0;symbol_num<16;symbol_num++) { if (symbol_num>5){ DispShowPos(0xE0); // 1110, 0000 DispShowSym(symbol_num, 1); }else{ DispShowSym(symbol_num, 0); } delayms(255); } DispScrClr(); delayms(255); } }
  60. 60. 練習2:點亮多顆顯示器(II) /************************************************************************** * @fn DispScrClr * @brief Clear the Display Screen. Clear all 7-Seg LEDs into blank * @param None * @return None *************************************************************************/ void DispScrClr(void) { SEG7LED = 0x00; seg_set = 1; seg_set = 0; POS7LED = 0xFF; pos_set = 1; pos_set = 0; } /************************************************************************* * @fn DispShowPos * @brief Show the Display Screen with assigned positions. * @param None * @return None ************************************************************************/ void DispShowPos(uint8 pos) { POS7LED = ~pos; pos_set = 1; pos_set = 0; } /********************************************************* * @fn DispShowSym * @brief Show the Symbol with dot-point(dp) or not ********************************************************/ void DispShowSym(uint8 sym_num, uint8 dp) { uint8 sym; if(dp!=0) sym = symbols[sym_num]|0x80; else sym = symbols[sym_num]; SEG7LED = sym; seg_set = 1; seg_set = 0; } /******************************************************** * @fn delayms * @brief delay with ms ********************************************************/ void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } } 60
  61. 61. 74HC573栓鎖器 61
  62. 62. 動態掃描同時顯示多位7段顯示器 62 pos_set=1; P0 = 0xFE; pos_set=0; seg_set=1; P0=0x5B; seg_set=0; delayms(2); pos_set=1; P0 = 0xFD; pos_set=0; seg_set=1; P0=0x7F; seg_set=0;  
  63. 63. 練習3:各位依序顯示數字0-7 #define POS(n) (1 << (n)) void DispSym_at_Pos(uint8 pos, uint8 symbol_num, uint8 dp); /*************************************************************************** * @fn DispSym_at_Pos * @brief Show the Symbol at specific position with dot-point(dp) or not * @param None * @return None ***************************************************************************/ void DispSym_at_Pos(uint8 pos, uint8 symbol_num, uint8 dp) { DispShowSym(symbol_num, dp); DispShowPos(pos); } void main() { DispScrClr(); uint8 symbol_num = 0; uint8 pos_num = 0; while(1) { for (pos_num=0;pos_num<8;pos_num++){ DispSym_at_Pos(POS(pos_num),symbol_num,0); symbol_num++; if (symbol_num ==8) symbol_num=0; delayms(250); } } } 63
  64. 64. 避免殘影 64 pos_set=1; P0 = 0xFE; pos_set=0; seg_set=1; P0=0x5B; seg_set=0; delayms(2); seg_set=1; P0=0x00; seg_set=0; delayms(1);   
  65. 65. 鍵盤鍵值對應 65 key_val keycode_bin keycode_hex 無 1111,0000 0xF0 0 0111,1110 0x7E 1 0111,1101 0x7D 2 0111,1011 0x7B 3 0111,0111 0x77 4 1011,1110 0xBE 5 1011,1101 0xBD 6 1011,1011 0xBB 7 1011,0111 0xB7 8 1101,1110 0xDE 9 1101,1101 0xDD 10 1101,1011 0xDB 11 1101,0111 0xD7 12 1110,1110 0xEE 13 1110,1101 0xED 14 1110,1011 0xEB 15 1110,0111 0xE7 0 4 8 C 1 5 9 D 2 6 A E 3 7 B F P3.0 P3.1 P3.2 P3.3 P3.7 P3.6 P3.5 P3.4 0 4 8 C 1 5 9 D 2 6 A E 3 7 B F P3.0 P3.1 P3.2 P3.3 P3.7 P3.6 P3.5 P3.4
  66. 66. 練習4:將按下掃描鍵盤值顯示出來 66 /********************************************************* Filename: exercise3-04.c Description: keypad scan *********************************************************/ #include<ioAT89C52.h> #define seg_set P2_bit.P2_6 #define pos_set P2_bit.P2_7 #define KeyPort P3 typedef unsigned char uint8; uint8 KeypadScan(void); uint8 KeyPushed(uint8 keycode); volatile uint8 n; void delayms(uint8 time); uint8 symbols[]={ 0x3F,0x06,0x5B,0x4F, 0x66,0x6D,0x7D,0x07, 0x7F,0x6F,0x77,0x7C, 0x39,0x5E,0x79,0x71}; void main() { // use POS4 of 7-seg LED to show the pushed key number P0=0xEF; pos_set=1; pos_set=0; while(1) { KeyPort = 0xF0; // H4-1(input), L4-0(output) if(KeyPort!=0xF0) { delayms(25); n = KeyPushed(KeypadScan()); } if(n!=0xFF) { P0=symbols[n]; seg_set=1; seg_set=0; }else{ P0=0x00; seg_set=1; seg_set=0; } } }
  67. 67. uint8 KeypadScan(void) { uint8 key_code; KeyPort = 0xFE; // 1111,1110: check col_1 if(KeyPort != 0xFE) { key_code = (KeyPort & 0xF0)|0x0E; delayms(1); return key_code; } KeyPort = 0xFD; // 1111,1101: check col_2 if(KeyPort != 0xFD) { key_code = (KeyPort & 0xF0)|0x0D; delayms(1); return key_code; } KeyPort = 0xFB; // 1111, 1011: check col_3 if(KeyPort != 0xFB) { key_code = (KeyPort & 0xF0)|0x0B; delayms(1); return key_code; } KeyPort = 0xF7; // 1111, 0111: check col_4 if(KeyPort != 0xF7) { key_code = (KeyPort & 0xF0)|0x07; delayms(1); return key_code; } return 0xFF; } uint8 KeyPushed(uint8 keycode) { switch(keycode) { case 0x7E: return 0; break; case 0x7D: return 1; break; case 0x7B: return 2; break; case 0x77: return 3; break; case 0xBE: return 4; break; case 0xBD: return 5; break; case 0xBB: return 6; break; case 0xB7: return 7; break; case 0xDE: return 8; break; case 0xDD: return 9; break; case 0xDB: return 10; break; case 0xD7: return 11; break; case 0xEE: return 12; break; case 0xED: return 13; break; case 0xEB: return 14; break; case 0xE7: return 15; break; default: return 0xFF; break; } } void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } } 67
  68. 68. 控制外部電路模組 Lab4:液晶顯示模組(LCM)
  69. 69. 1602A LCM模組 RS R/W 作用 0 0 將指令碼寫入IR(指令暫存器),並執行指令。 0 1 讀取BF(DB7)與位址計數器AC(DB0~6)內容。 1 0 將資料寫入DR(資料暫存器)。 LCM內部自動執行DR->DD RAM或DR->CG RAM。 1 1 讀取DR之資料。 LCM內部自動執行DD RAM->或CG RAM->DR。 69 MC U
  70. 70. 字元圖形顯示到螢幕的過程 70 ‘I’, ‘ ’, ‘a’, ‘m’, … (ASCII) 文字擺放的 起始位置 I a m a t e a c h e 只要指定起始位置,每個字元寫入 後,AC就會自動累加(減),因此所 有字元就會依序顯示出來。
  71. 71. 標準字符庫(字元產生器CGROM) 71
  72. 72. CG RAM (自製字元圖形) static uint8 LCD_DispBar1[] = {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}; static uint8 LCD_DispBar2[] = {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}; static uint8 LCD_DispBar3[] = {0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C}; static uint8 LCD_DispBar4[] = {0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E}; static uint8 LCD_DispBar5[] = {0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}; /********************************************************** * @fn LCD_DefChar * @brief This function help you define your own pattern * @param uint8 id: offset of the CGRAM, uint8 *pat points * to the pattern table (array). * @return None ***********************************************************/ void LCD_DefChar(uint8 id, uint8 *pat) { uint8 i; LCD_CmdWr(0x40 + ( id<<3 )); // Set address of CGRAM for(i=0;i<8;i++){ LCD_DataWr(*pat++); } } 72
  73. 73. LCM共有11種指令 73 指令 動作 RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 執行 時間 功能設定 0 0 0 0 1 DL(1) N(2) F(3) X X 40 us 清除顯示器 0 0 0 0 0 0 0 0 0 1 1.64ms 游標回左上角 0 0 0 0 0 0 0 0 1 X 40 us 設定輸入模式 0 0 0 0 0 0 0 1 I/D(4) S(4) 40 us 螢幕開或關 0 0 0 0 0 0 1 D C B 40 us 游標/顯示移位 0 0 0 0 0 1 S/C(5) R/L(5) X X 40 us 設定CGRAM地址 0 0 0 1 A A A A A A 40 us 設定DDRAM地址 0 0 1 A A A A A A A 40 us 讀取BF與地址 0 1 BF(6) A A A A A A A 40 us 寫資料至RAM 1 0 D D D D D D D D 40 us 從RAM讀資料 1 1 D D D D D D D D 40 us
  74. 74. 日立HD44780相容LCD之常用指令表 74 No. Instruction Hex Decimal 1 Function Set: 8-bit, 1 Line, 5x7 Dots 0x30 48 2 Function Set: 8-bit, 2 Line, 5x7 Dots 0x38 56 3 Function Set: 4-bit, 1 Line, 5x7 Dots 0x20 32 4 Function Set: 4-bit, 2 Line, 5x7 Dots 0x28 40 5 Entry Mode 0x06 6 6 Display off Cursor off (clearing display without clearing DDRAM content) 0x08 8 7 Display on Cursor on 0x0E 14 8 Display on Cursor off 0x0C 12 9 Display on Cursor blinking 0x0F 15 10 Shift entire display left 0x18 24 12 Shift entire display right 0x1C 30 13 Move cursor left by one character 0x10 16 14 Move cursor right by one character 0x14 20 15 Clear Display (also clear DDRAM content) 0x01 1 16 Set DDRAM address or cursor position on display 0x80+add 128+add 17 Set CGRAM address or set pointer to CGRAM location 0x40+add 64+add
  75. 75. 讀取時序圖 75
  76. 76. 寫入時序圖 76
  77. 77. 實習4 77  練習1:撰寫LCM驅動程式,系統開機於螢幕顯示 字串,按下按鍵改變顯示字串內容。  練習2:螢幕的動態效果  練習3:使用自訂圖形。進度條的製作。
  78. 78. 練習1:撰寫LCM驅動程式 78 #include<ioAT89C52.h> #define BTN1 P3_bit.P3_2 #define BTN2 P3_bit.P3_3 #define LCD_RW P1_bit.P1_1 #define LCD_EN P3_bit.P3_4 #define LCD_RS P3_bit.P3_5 #define LCD_BF P0_bit.P0_7 #define LCD_DATA_PORT P0 #define LCD_SEL_CMD 0 #define LCD_SEL_DATA 1 #define LCD_IO_WRITE 0 #define LCD_IO_READ 1 /* HD44780 Commands */ #define LCD_CMD_CLS 0x01 #define LCD_CMD_FNCT 0x38 // 16*2 display, 5*7 bitmap, 8-bits data port #define LCD_CMD_MODE 0x06 // after R/W one char, addr+1, cursor+1 #define LCD_CMD_ON_OFF 0x0C // Enable LCD, Cursor OFF, No Blink Cursor typedef unsigned char uint8;
  79. 79. 79 static uint8 LCD_MaxCols; static uint8 LCD_MaxRows; static uint8 LCD_DispBar1[] = {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10}; static uint8 LCD_DispBar2[] = {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18}; static uint8 LCD_DispBar3[] = {0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C}; static uint8 LCD_DispBar4[] = {0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E}; static uint8 LCD_DispBar5[] = {0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F}; char StrL1[]="LCD 1602 Test"; char StrL2[]="OK"; static void LCD_CursorSet(uint8 row, uint8 col); void LCD_DataWr(uint8 data); void LCD_CmdWr(uint8 cmd); void LCD_Init(uint8 maxrows, uint8 maxcols); void LCD_DispChar(uint8 row, uint8 col, char c); void LCD_DispStr(uint8 row, uint8 col, char *s); void LCD_ClrLine(uint8 line); void LCD_ClrScr(void); void LCD_DefChar(uint8 id, uint8 *pat); void LCD_DispHorBarInit(void); void LCD_DispHorBar(uint8 row, uint8 col, uint8 val); void delayms(uint8 time);
  80. 80. 80 /********************************************************************************** * @fn LCD_Init * @brief LCD Initilization * @param uint8 maxrows: max number of rows, uint8 maxcols: max number of columns * @return None **********************************************************************************/ void LCD_Init(uint8 maxrows, uint8 maxcols) { LCD_MaxCols = maxcols; LCD_MaxRows = maxrows; delayms(30); LCD_EN = 0; LCD_RS = 0; LCD_RW = 0; LCD_CmdWr(LCD_CMD_FNCT); delayms(5); LCD_CmdWr(LCD_CMD_FNCT); delayms(5); LCD_CmdWr(LCD_CMD_FNCT); delayms(5); LCD_CmdWr(LCD_CMD_FNCT); LCD_CmdWr(LCD_CMD_ON_OFF); LCD_CmdWr(LCD_CMD_MODE); LCD_CmdWr(LCD_CMD_CLS); delayms(2); } /***************************************** * @fn LCD_CmdWr * @brief Write a command to the LCD * @param uint8 cmd: the command * @return None *****************************************/ void LCD_CmdWr(uint8 cmd) { delayms(1); LCD_RS = LCD_SEL_CMD; LCD_RW = LCD_IO_WRITE; LCD_DATA_PORT = cmd; delayms(1); LCD_EN = 1; delayms(1); LCD_EN = 0; }
  81. 81. 81 /******************************************************************************************* * @fn LCD_ClrLine * @brief Clear a certain line * @param uint8 line: 1 is the first line, and 2 is the second line * @return None ******************************************************************************************/ void LCD_ClrLine(uint8 line) { uint8 i; if(line < LCD_MaxRows) { LCD_CursorSet(line, 0); for(i=0;i<LCD_MaxCols;i++){ LCD_DataWr(' '); } LCD_CursorSet(line, 0); } } /************************************ * @fn LCD_ClrScr * @brief Clear whole screen * @param None * @return None ***********************************/ void LCD_ClrScr(void) { LCD_CmdWr(LCD_CMD_CLS); } /*********************************************** * @fn LCD_DataWr * @brief Write a data to the LCD * @param uint8 data: the data * @return None **********************************************/ void LCD_DataWr(uint8 data) { delayms(1); LCD_RS = LCD_SEL_DATA; LCD_RW = LCD_IO_WRITE; LCD_DATA_PORT = data; delayms(1); LCD_EN = 1; delayms(1); LCD_EN = 0; }
  82. 82. 82 /*********************************************************************************** * @fn LCD_CursorSet * @brief Set the position of the cursor * @param (uint8 row, uint8 col) sets the position * @return None **********************************************************************************/ static void LCD_CursorSet(uint8 row, uint8 col) { switch(row){ case 0: if(LCD_MaxRows==1){ if (col < (LCD_MaxCols >> 1)) // If used only one line LCD LCD_CmdWr(0x80 + col); // First half of line starts at 0x80 else // Second half of line starts at 0xC0 LCD_CmdWr(0xC0 + col - (LCD_MaxCols >> 1)); }else{ LCD_CmdWr(0x80 + col); // select line 1 } break; case 1: LCD_CmdWr(0xC0 + col); // select line 2 break; case 2: LCD_CmdWr(0x80 + LCD_MaxCols + col); // select line 3 break; case 3: LCD_CmdWr(0xC0 + LCD_MaxCols + col); // select line 4 break; } }
  83. 83. 83 /************************************************************************************* * @fn LCD_DispChar * @brief Show a character at specific postion * @param (uint8 row, uint8 col) is the postion, and char c: the character to show * @return None ************************************************************************************/ void LCD_DispChar(uint8 row, uint8 col, char c) { if(row < LCD_MaxRows && col < LCD_MaxCols) { LCD_CursorSet(row, col); LCD_DataWr(c); } } /*********************************************************************************** * @fn LCD_DispStr * @brief Display a string from a specific position * @param (uint8 row, uint8 col) sets the position and char *s is the string * @return None *********************************************************************************/ void LCD_DispStr(uint8 row, uint8 col, char *s) { uint8 i; if(row < LCD_MaxRows && col < LCD_MaxCols){ LCD_CursorSet(row, col); i = col; while( i < LCD_MaxCols && *s){ LCD_DataWr(*s++); i++; } } }
  84. 84. /*************************************************************************************************** * @fn LCD_DispHorBar * @brief Display the horizon bar * @param (uint8 row, uint8 col) sets the position, uint8 val sets the value of full (100%) * @return None ***************************************************************************************************/ void LCD_DispHorBar(uint8 row, uint8 col, uint8 val) { uint8 i, full, frac; full = val/5; // how many full blocks to turn on frac = val%5; // portion of block if(row < LCD_MaxRows && (col + full -1) < LCD_MaxCols){ i = 0; LCD_CursorSet(row, col); while(full > 0) { LCD_DataWr(5); // Send Custom character #5, which is full block i++; full--; } if(frac>0){ LCD_DataWr(frac); // custom char #frac } } } /************************************** * @fn LCD_DispHorBarInit * @brief HorizonBar Initilize * @param None * @return None *************************************/ void LCD_DispHorBarInit(void) { LCD_DefChar(1, &LCD_DispBar1[0]); LCD_DefChar(2, &LCD_DispBar2[0]); LCD_DefChar(3, &LCD_DispBar3[0]); LCD_DefChar(4, &LCD_DispBar4[0]); LCD_DefChar(5, &LCD_DispBar5[0]); } 84
  85. 85. 85 /********************************************************** * @fn LCD_DefChar * @brief This function help you define your own pattern * @param uint8 id: offset of the CGRAM, uint8 *pat points * to the pattern table (array). * @return None ***********************************************************/ void LCD_DefChar(uint8 id, uint8 *pat) { uint8 i; LCD_CmdWr(0x40 + ( id<<3 )); // Set address of CGRAM for(i=0;i<8;i++){ LCD_DataWr(*pat++); } } /******************************************************** * @fn delayms * @brief delay with ms * @param time = 0 ~ 255, the maximum delay is 255 ms * @return None ********************************************************/ void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } }
  86. 86. 86 void main() { LCD_Init(2, 16); LCD_DispStr(0,0,StrL1); LCD_DispStr(1,0,StrL2); BTN1 = 1; BTN2 = 1; while(1) { if (BTN1 == 0) { delayms(50); if (BTN1 == 0) { while(BTN1 == 0); LCD_ClrScr(); } LCD_DispStr(0,0,StrL1); } if (BTN2 == 0) { delayms(50); if (BTN2 == 0) { while(BTN2 == 0); LCD_ClrScr(); LCD_DispStr(1,0,"ROW1, COL0"); LCD_DispStr(0,1,"ROW0, COL1"); } } } }
  87. 87. 練習2:動態效果 87 void main() { uint8 i = 0, j; BTN1 = 1; char *str1 = "Dynamic Show"; char *str2 = "rolling text"; LCD_Init(2, 16); LCD_DispStr(0, 0, StrL1); for(j=0;j<3;j++) { delayms(250); } LCD_DispStr(1, 0, StrL2); for(j=0;j<8;j++) { delayms(250); } LCD_ClrScr(); delayms(2); LCD_DispStr(0, 0, str1); for(j=0;j<16;j++) { LCD_CmdWr(0x1C); delayms(250); } for(j=0;j<16;j++) { LCD_CmdWr(0x18); delayms(250); } delayms(250); while(1) { if (BTN1 == 0) { delayms(50); if (BTN1 == 0) { while(BTN1 == 0); LCD_ClrLine(0); while(*(str2+i)) { LCD_DispChar(0, i, *(str2+i)); i++; delayms(200); } i=0; } } } }
  88. 88. 練習3:進度條製作 88 void main() { uint8 i, percent_bar; char num[4]; BTN1 = 1; char *str1 = "Dynamic Show"; LCD_Init(2, 16); LCD_DispHorBarInit(); LCD_DispStr(0, 0, str1); while(1) { if (BTN1 == 0) { delayms(50); if (BTN1 == 0) { while(BTN1 == 0); LCD_ClrLine(0); LCD_ClrLine(1); for(i=0;i<101;i++) { num[0] = (i/100)+0x30; num[1] = ((i/10)%10)+0x30; num[2] = (i%10)+0x30; num[3] = 0x25; if(num[0]==0x30) { LCD_DispChar(0, 0, ' '); if(num[1]==0x30) LCD_DispChar(0, 1, ' '); else LCD_DispChar(0, 1, num[1]); } else { LCD_DispChar(0, 0, num[0]); LCD_DispChar(0, 1, num[1]); } LCD_DispChar(0, 2, num[2]); LCD_DispChar(0, 3, num[3]); percent_bar = (i*100/125); LCD_DispHorBar(1, 0, percent_bar); delayms(200); } } } } }
  89. 89. 中斷系統 Lab5: 使用按鍵引發中斷
  90. 90. 中斷的概念 RETI …… 90
  91. 91. 中斷的優缺點  優點: 1) 解決快速主機與慢速I/O設備的資料傳送問題。 2) CPU可分時為多個I/O設備服務,提高電腦利用率。 3) 即時性。 4) 可處理設備故障及掉電等突發事件,使系統可靠性提高。  缺點: 1) ISR程式不可執行太久 2) 不可於ISR中呼叫太多函式 3) 有critical section的問題 4) 不能呼叫不可重進入函式 5) 巢狀中斷可能使堆疊溢位 91
  92. 92. C51中斷結構:暫存器IE/TCON/IP EX0 EA PX0 0 1 ET0 PT0 0 1 EX1 PX1 0 1 ET1 PT1 0 1 ES PS 0 1 ≥1 RI TI SCON TCON IE0 TF0 IE1 TF1 10 1 0 1 IT0 IT1 INT0 INT1 T0 T1 RX TX IE IP 1 11 1 1 1 1 1 0 92
  93. 93. 中斷入口 /* Interrupt Vectors */ #define extern0 0x03 /* External interrupt 0 */ #define IE0_int 0x03 /* External interrupt 0 */ #define timer0 0x0B /* Timer 0 Interrupt */ #define TF0_int 0x0B /* Timer 0 Interrupt */ #define extern1 0x13 /* External interrupt 1 */ #define IE1_int 0x13 /* External interrupt 1 */ #define timer1 0x1B /* Timer 1 Interrupt */ #define TF1_int 0x1B /* Timer 1 Interrupt */ ... 中斷源 中斷旗標 ISR入口 優先權 外部中斷0 IE0 0x0003 高 定時/計數器0 (T0) TF0 0x000B 外部中斷1 IE1 0x0013 定時/計數器0 (T1) TF1 0x001B 串列埠 RI或TI 0x0023 低 93
  94. 94. 範例實習5  請打開lab5_Interrupt  練習1: 外部中斷0  練習2: 觀察中斷打斷目前工作  練習3: 優先權的設置  練習4: 在ISR中更改全域變數的問題  練習5: 在ISR中使用不可重進入函式的問題 94
  95. 95. 練習1:使用外部中斷0 95 /************************************************************************************ Filename: exercise5-01.c Description: 使用button引發中斷, 按壓BTN1以點亮或熄滅LED0 開發板按鍵{INT0, INT1, T0, T1}, 分別對應到 {P3_2, P3_3, P3_4, P3_5} *************************************************************************************/ #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 #define BTN1 P3_bit.P3_2 typedef unsigned char uint8; void delayms(uint8); void main() { BTN1 = 1; // I/O要當輸入時,要先寫入1 LED0 = 1; TCON_bit.IE0 = 0; // 外部中斷0的中斷旗標設為0 IE_bit.EX0 = 1; // 外部中斷0致能 IE_bit.EA = 1; // 總中斷致能 while(1) { ; } } void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } } /* Symbol defined in I/O header file */ #pragma vector = extern0 __interrupt void Int_Extern0(void) { LED0 = ~LED0; TCON_bit.IE0 = 0; // 外部中斷0的中斷旗標清為0 } // 如果按壓按鈕的效果不好, 請加入debounce程式 // 練習: 改以外部中斷1實作
  96. 96. 練習2:中斷會打斷當前工作 96  平時LED1閃爍,按BTN1發生中斷使LED0閃爍10次 #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 #define LED1 P1_bit.P1_1 #define BTN1 P3_bit.P3_2 typedef unsigned char uint8; void delayms(uint8); void main() { BTN1 = 1; // I/O要當輸入時,要先寫入1 LED0 = 1; LED1 = 1; TCON_bit.IE0 = 0; // 外部中斷0的中斷旗標設為0 IE_bit.EX0 = 1; // 外部中斷0致能 IE_bit.EA = 1; // 總中斷致能 while(1) { delayms(250); delayms(250); LED1 = ~LED1; } } #pragma vector = extern0 __interrupt void Int_Extern0(void) { uint8 i; for(i=0;i<20;i++){ LED0 = ~LED0; delayms(250); } TCON_bit.IE0 = 0; // 外部中斷0的中斷旗標清為0 }
  97. 97. 練習3:優先權 97  LED3平時閃爍,按BTN1, LED0閃10次, BTN2則LED1 快閃15次 #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 #define LED1 P1_bit.P1_1 #define LED2 P1_bit.P1_2 #define BTN1 P3_bit.P3_2 #define BTN2 P3_bit.P3_3 typedef unsigned char uint8; void delayms(uint8); void main() { BTN1 = 1; BTN2 = 1; LED0 = 1; LED1 = 1; TCON_bit.IE0 = 0; // 外部中斷0的中斷旗標設為0 IE_bit.EX0 = 1; // 外部中斷0致能 IP_bit.PX0 = 1; IP_bit.PX1 = 0; TCON_bit.IE1 = 0; // 外部中斷1的中斷旗標設為0 IE_bit.EX1 = 1; // 外部中斷1致能 IE_bit.EA = 1; // 總中斷致能 while(1) { delayms(250); delayms(250); LED2 = ~LED2; } } void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } } #pragma vector = extern0 __interrupt void Int_Extern0(void) { uint8 i; for(i=0;i<20;i++){ LED0 = ~LED0; delayms(250); } TCON_bit.IE0 = 0; // 外部中斷0的中斷旗標清為0 } #pragma vector = extern1 __interrupt void Int_Extern1(void) { uint8 i; for(i=0;i<30;i++){ LED1 = ~LED1; delayms(100); } TCON_bit.IE1 = 0; // 外部中斷1的中斷旗標清為0 }
  98. 98. 練習4:全域變數 98  BTN2按完更改gflag以決定LED1之亮滅 typedef unsigned char uint8; volatile uint8 gflag = 0; void delayms(uint8); #pragma vector = extern0 __interrupt void Int_Extern0(void) { uint8 i; for(i=0;i<20;i++){ LED0 = ~LED0; delayms(250); } // gflag = ~gflag; TCON_bit.IE0 = 0; // 外部中斷0的中斷旗標清為0 } #pragma vector = extern1 __interrupt void Int_Extern1(void) { uint8 i; for(i=0;i<30;i++){ LED1 = ~LED1; delayms(100); } gflag = ~gflag; TCON_bit.IE1 = 0; // 外部中斷1的中斷旗標清為0 } #pragma vector = extern0 __interrupt void Int_Extern0(void) { IE_bit.EA = 0; uint8 i; for(i=0;i<20;i++){ LED0 = ~LED0; delayms(250); } gflag = ~gflag; TCON_bit.IE0 = 0; // 外部中斷0的中斷旗標清為0 IE_bit.EA = 1; } #pragma vector = extern1 __interrupt void Int_Extern1(void) { IE_bit.EA = 0; uint8 i; for(i=0;i<30;i++){ LED1 = ~LED1; delayms(100); } gflag = ~gflag; TCON_bit.IE1 = 0; // 外部中斷1的中斷旗標清為0 IE_bit.EA = 1; }
  99. 99. 練習5:不可重進入函數 99 /********************************************************************************** Filename: exercise5-05.c Description: 不可重進入函數 開發板按鍵{INT0, INT1, T0, T1}, 分別對應到 {P3_2, P3_3, P3_4, P3_5} ************************************************************************************/ #include<ioAT89C52.h> #define LED0 P1_bit.P1_0 #define LED1 P1_bit.P1_1 #define LED2 P1_bit.P1_2 #define BTN1 P3_bit.P3_2 typedef unsigned char uint8; void delayms(uint8); volatile uint8 temp; void swap(uint8* x, uint8*y); void main() { BTN1 = 1; LED0 = 1; LED1 = 1; TCON_bit.IE0 = 0; // 外部中斷0的中斷旗標設為0 IE_bit.EX0 = 1; // 外部中斷0致能 IE_bit.EA = 1; // 總中斷致能 uint8 a = 1; uint8 b = 0; while(1) { if(a==1) LED1 = 0; else if (a==0) LED1 = 1; else LED2 = 0; delayms(250); swap(&a, &b); } } void swap(uint8* x, uint8* y) { uint8 i; temp = *x; LED0 = 0; for(i=0;i<10;i++) delayms(250); LED0 = 1; *x = *y; *y = temp; } void delayms(uint8 time) { uint8 n; while(time>0) { n = 162; while(n>0) n--; time --; } } #pragma vector = extern0 __interrupt void Int_Extern0(void) { IE_bit.EA = 0; uint8 a = 5, b =10; swap(&a, &b); TCON_bit.IE0 = 0; IE_bit.EA = 1; }

×