# 雞巴微處理機 太多東西 其他放在 [雞巴微處理機2](https://hackmd.io/W7gQ3FF9ThynDiRpXadflA) ![image](https://hackmd.io/_uploads/B1BSpaxEJx.png) ## 10/4 GPIO(RGBLED & Interrupt) :::spoiler # Lab 3 - GPIO(RGBLED & Interrupt) ## 彩色燈 - 初始狀態下 RGBLED 不亮燈。 - 按下 R/G/B 鍵控制紅/綠/藍光的開關。 - 按下 C 所有燈關閉。 - 即便按鍵長按,對應的動作只會觸發 1 次。 | t | Key | color | |-----|-----|-------| | 0 | | (off) | | 1 | R | Red | | 2 | G | Yellow| | 3 | R | Green | | 4 | B | Cyan | | 5 | R | White | | 6 | C | (off) | | ... | ... | ... | ### 鍵位配置 ``` ┌───┬───┬───┐ │ │ │ │ ├───┼───┼───┤ │ R │ G │ B │ ├───┼───┼───┤ │ │ C │ │ └───┴───┴───┘ ``` <!-- ## 行人優先號誌 在某個商圈有一條大型的商店街,在這條街上僅限行人通行,該地的商業活動繁榮;商店街的中間與四線道馬路形成路口,雖然兩側有天橋連接,但為了能使龐大的人流與身障人士過馬路,天橋底下也有寬 12 米的行人穿越道,在此處有設按鈕號誌,並設計下列的時向變化: - 時相 0: 沒按下按鈕時的 stand by 時相。 - 行車方向號誌為黃燈閃爍。 - 按下按鈕後,轉換成時相 1。 - 時相 1: 行車通行: - 通行時間:綠燈 10 秒 + 閃爍綠燈 5 秒鐘。 - 淨空時間:黃燈 3 秒 + 紅燈 3 秒。 - 時向 2: 行人通行: --> <!-- ## 按鈕式號誌(分段給分) 在一處行人徒步區與一條四線道的路口相交,行人有頻繁過馬路的需求。雖然行經的車輛有義務停讓行人優先通行,但為了讓行動不便者也能安心過馬路,在這個路口設有按鈕式號誌(俗稱紅綠燈),按下去後提供行人有足夠的專用時相通過馬路。 - 以 RGBLED 作為行車號誌、LED5~8 作為行人號誌、INT1 作為觸發按鈕。 - 「初始狀態」按下按鈕後狀態以「時相 1」->「時相 2」->「初始狀態」的順序變化,其餘時間按下則無反應。 - 「初始狀態」行車號誌以黃燈與行人號誌互相閃爍。 - 「時相 1」行人號誌紅燈(亮燈),行車號誌的變化為: 1. 綠燈 10 秒。 2. 閃爍綠燈 5 秒。 3. 黃燈 3 秒。 4. 紅燈 3 秒(路口淨空)。 - 「時相 2」行車號誌紅燈,行人號誌的變化為: 1. 綠燈(以不亮燈代替) 30 秒。 2. 閃爍綠燈 15 秒。 3. 紅燈 5 秒。 - 在七段顯示器最左邊顯示目前的時相、右邊兩位顯示該時向綠燈(含閃爍)剩餘時間。 | t(sec.) | button | motor | pedestrian | 7-seg | note | |---------|--------|-----------------|------------|----------|---------------| | ~0 | | flashing yellow | flash | `0 00` | stand by\*1. | | 0~10 | press | green | on | `1 xx`\*2| phase 1 start | | 10~15 | | flashing green | on | `1 xx` | | | 15~18 | | yellow | on | `1 00` | | | 18~21 | | red | on | `1 00` | phase 1 end | | 21~51 | | red | off | `2 xx` | phase 2 start | | 51~66 | | red | flash | `2 xx` | | | 66~71 | | red | on | `2 00` | phase 2 end | | 71~ | | flashing yellow | flash | `0 00` | stand by | \*1. In "stand by", two signals should **NOT** be on at the same time. \*2. `xx` is the current value of down counter of green. ### 分段給分說明 - 檢查目前的階段時,前面的要求要一同達到才可計分。 - 可以先檢查完第一階段再做第二階段;若直接檢查第二階段通過會一同給予第一階段的成績。 #### 第一階段 按下按鈕後,可以進入到「時相 1」且可以看到以下結果: - RGBLED 從閃黃燈變成綠燈。 - 七段顯示器顯示 `1 45` 並每秒 -1。 #### 第二階段 完成所有功能。 --> --- ## 紅綠燈 ![image alt](https://upload.wikimedia.org/wikipedia/commons/9/90/A_Traffic_Signal_Head_in_Taipei_City%2C_Taiwan%2C_Sep_2014.jpg) - 在 RGBLED 熄滅的時候按下 INT1,RGBLED 依序顯示「綠燈 15 秒」 -> 「黃燈 5 秒」 -> 「紅燈 6 秒」,然後熄滅;若 RGBLED 燈有亮時按下 INT1 流程仍要繼續完成。 - 在右邊 2 個七段顯示器上顯示目前燈號的剩餘秒數;若為熄滅顯示 `..00`。 | t | INT1 | color | 7-seg | |-------|----------|--------|----------| | ~0 | | (off) | `..00`\*1| | 0~15 | press | green | `..xx`\*2| | 15~20 | | yellow | `..xx` | | 20~26 | | red | `..xx` | \*1. The `.` mean that the digit is empty. \*2. The `xx` is the decimal value of the down counter. ### 提醒 外部中斷用法可參考 sample code `GPIO_ExtInt`,另外你需要在 `MCU_init.h` 中加上 `#define MCU_INTERFACE_INT1_PB15`,改完後的 `MCU_init.h` 會像是這個樣子: ```clike= //Define Clock source #define MCU_CLOCK_SOURCE #define MCU_CLOCK_SOURCE_HXT // HXT, LXT, HIRC, LIRC #define MCU_CLOCK_FREQUENCY 50000000 //Hz //Define MCU Interfaces #define MCU_INTERFACE_INT1_PB15 ``` ::: ### 第一題 ```cpp= // // GPIO_RGBLED: GPIO output to control R/G/B LED // // PA12 : NUC140VE3CN to control blue LED // PA13 : NUC140VE3CN to control green LED // PA14 : NCU140VE3CN to control red LED #include <stdio.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" void Init_GPIO(void) { GPIO_SetMode(PA, BIT12, GPIO_MODE_OUTPUT); GPIO_SetMode(PA, BIT13, GPIO_MODE_OUTPUT); GPIO_SetMode(PA, BIT14, GPIO_MODE_OUTPUT); PA12=1; PA13=1; PA14=1; } int32_t main (void) { int k,tmpkey=0; SYS_Init(); Init_GPIO(); PA12=1; PA13=1; PA14=1; // PA12 = Blue, 0 : on, 1 : off // PA13 = Green, 0 : on, 1 : off // PA14 = Red, 0 : on, 1 : off while(1){ k=ScanKey(); CLK_SysTickDelay(3000); k=ScanKey(); CLK_SysTickDelay(3000); if(k==ScanKey()){//解決按鈕彈跳 if(k!=tmpkey){ if(k==4){ if(PA14==1)PA14=0; else PA14=1; }else if(k==5){ if(PA13==1)PA13=0; else PA13=1; }else if(k==6){ if(PA12==1)PA12=0; else PA12=1; }else if(k==8){ PA12=1; PA13=1; PA14=1; } } tmpkey=k; } } } ``` ### 第二題 ```cpp= // // GPIO_EXTINT : External Interrupt Pins to interrupt MCU // // EVB : Nu-LB-NUC140 // MCU : NUC140VE3CN // INT0 /PB14 : NUC140 LQFP100 pin4 // INT1 /PB15 : NUC140 LQFP100 pin91 #include <stdio.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" int hold=0; void Display_7seg(uint16_t value) { uint8_t digit; digit = value / 10; CloseSevenSegment(); ShowSevenSegment(1,digit); CLK_SysTickDelay(5000); value = value - digit * 10; digit = value; CloseSevenSegment(); ShowSevenSegment(0,digit); CLK_SysTickDelay(5000); } void EINT1_IRQHandler(void) { GPIO_CLR_INT_FLAG(PB, BIT15); // Clear GPIO interrupt flag hold=1; } void Init_EXTINT(void) { // Configure EINT1 pin and enable interrupt by rising and falling edge trigger GPIO_SetMode(PB, BIT15, GPIO_MODE_INPUT); GPIO_EnableEINT1(PB, 15, GPIO_INT_RISING); // RISING, FALLING, BOTH_EDGE, HIGH, LOW NVIC_EnableIRQ(EINT1_IRQn); // Enable interrupt de-bounce function and select de-bounce sampling cycle time GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_64); GPIO_ENABLE_DEBOUNCE(PB, BIT15); } int32_t main() { int i,j; SYS_Init(); OpenSevenSegment(); Init_EXTINT(); while(1){ PA12=1; PA13=1; PA14=1; if(hold){ for(i=0;i<26;i++){ CloseSevenSegment(); if(i>=0 && i<15){ for(j=0;j<50;j++){ Display_7seg(15-i); PA13=0; } }else if(i>=15 && i<20){ for(j=0;j<50;j++){ Display_7seg(5-i+15); PA13=0; PA14=0; } }else if(i>=20 && i<26){ for(j=0;j<50;j++){ Display_7seg(6-i+20); PA13=1; PA14=0; } } hold=0; } }else{ Display_7seg(0); } } } ``` ## 10/18 Text on LCD :::spoiler # Lab 4 - Text on LCD ## 題目 1 - IPv4 位址檢查 ![image alt](https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/IPv4_address_structure_and_writing_systems-en.svg/1920px-IPv4_address_structure_and_writing_systems-en.svg.png) 輸入 4 個三位數字,檢查是否為合理的 IPv4 位址。 - 固定在 LCD 第一行顯示 "IPv4 Checker"。 - 利用 Keypad 與 INT1 連續輸入 3 個數字到一個欄位,重複 4 次,在 LCD 第 3 行顯示目前輸入的狀況,每個欄位需要用 "." 隔開。 - 輸入完 12 個數字後,若格式正確在第四行顯示 "OK",否則顯示 "ERROR"。 - 重新輸入 12 數字第 4 行清空。 - 每按一次按鍵只能當成輸入 1 個數字。 ### 按鍵配置 ``` ┌───┬───┬───┐ │ 1 │ 2 │ 3 │ ├───┼───┼───┤ │ 4 │ 5 │ 6 │ ├───┼───┼───┤ │ 7 │ 8 │ 9 │ INT1: 0 └───┴───┴───┘ ``` ### 範例 初始狀態 ``` ┌────────────────┐ │IPv4 Checker │ │ │ │ │ │ │ └────────────────┘ ``` 按下 1 ``` ┌────────────────┐ │IPv4 Checker │ │ │ │1 │ │ │ └────────────────┘ ``` 按下 9 ``` ┌────────────────┐ │IPv4 Checker │ │ │ │19 │ │ │ └────────────────┘ ``` 按下 2 ``` ┌────────────────┐ │IPv4 Checker │ │ │ │192. │ │ │ └────────────────┘ ``` 按下 1 ``` ┌────────────────┐ │IPv4 Checker │ │ │ │192.1 │ │ │ └────────────────┘ ``` ***(中間略過)*** 按下 0 ``` ┌────────────────┐ │IPv4 Checker │ │ │ │192.168.000.00 │ │ │ └────────────────┘ ``` 按下 1 ``` ┌────────────────┐ │IPv4 Checker │ │ │ │192.168.000.001 │ │OK │ └────────────────┘ ``` 按下 INT1 ``` ┌────────────────┐ │IPv4 Checker │ │ │ │0 │ │ │ └────────────────┘ ``` ***(中間略過)*** 按下 7 ``` ┌────────────────┐ │IPv4 Checker │ │ │ │086.109.092.427 │ │ERROR │ └────────────────┘ ``` ## 題目 2 - 視覺化最小堆積樹 ![](https://upload.wikimedia.org/wikipedia/commons/thumb/2/28/%27Ommelozen_boom%27.jpg/800px-%27Ommelozen_boom%27.jpg?20181106210149) 利用陣列 `arr` 製作上限為 `n` 個節點的「最小堆積樹」有以下特性: 1. heap 是一種 complete tree。 2. heap 的根節點為 `arr[0]`。 3. `arr[i]` 的父節點為 `arr[(i-1)/2]`、子節點為 `arr[i*2+1]`, `arr[i*2+2]`(如果有父/子節點)。 請在 LCD 顯示最小堆積樹。 - 初始狀態下 LCD 的第 3 行顯示 `"Hello World!!"`、第 4 行顯示 `from LCD_minHeap.`;同時間 heap 為空的。 - 按下 Keypad 上的按鍵,若 heap 中數值量未達上限(7 個),將對應的數值加入 heap 中並在 LCD 第 4 行顯示 `Push x.`;若數值量達上限則顯示 `Heap is FULL!!`。 - 按下 INT1 ,若 heap 非空,將 `arr[0]` 的數值 pop 出來並在 LCD 第 4 行顯示 `Pop x.`;若 heap 空則顯示 `Heap is EMPTY!!`。 - 在 LCD 前三行顯示 heap 目前的樣子。 **註 1.**:不管你怎麼實作 min heap ,顯示只要符合 min heap 的特性即可。 **註 2.**:建議至少要寫出以下 function。 ```clike typedef struct{...} Heap; Heap* create_empty_heap(...); void push(Heap* heap, int value); int pop(Heap* heap); ``` ### 按鍵配置 ``` ┌───┬───┬───┐ │ 1 │ 2 │ 3 │ ├───┼───┼───┤ │ 4 │ 5 │ 6 │ ├───┼───┼───┤ │ 7 │ 8 │ 9 │ INT1:pop └───┴───┴───┘ ``` ### 範例 初始狀態 ``` ┌────────────────┐ │ │ │ │ │"Hello World!!" │ │from LCD_minHeap│ └────────────────┘ ``` 按下 INT1 ``` ┌────────────────┐ │ │ │ │ │ │ │Heap is EMPTY!! │ └────────────────┘ ``` 按下 4 ``` ┌────────────────┐ │ 4 │ │ │ │ │ │Push 4. │ └────────────────┘ ``` 按下 7 ``` ┌────────────────┐ │ 4 │ │ 7 │ │ │ │Push 7. │ └────────────────┘ ``` ***(中間略過)*** <!-- 按下 3 ``` ┌─────────────────┐ │ 3 │ │ 7 4 │ │ │ │ push 3 │ └─────────────────┘ ``` 按下 4 ``` ┌─────────────────┐ │ 3 │ │ 4 4 │ │ 7 │ │ push 4 │ └─────────────────┘ ``` 按下 2 ``` ┌─────────────────┐ │ 2 │ │ 3 4 │ │ 7 4 │ │ push 2 │ └─────────────────┘ ``` 按下 5 ``` ┌─────────────────┐ │ 2 │ │ 3 4 │ │ 7 4 5 │ │ push 5 │ └─────────────────┘ ``` --> 按下 4 ``` ┌────────────────┐ │ 1 │ │ 3 2 │ │ 7 4 5 4 │ │Push 4. │ └────────────────┘ ``` 按下 6 ``` ┌────────────────┐ │ 1 │ │ 3 2 │ │ 7 4 3 4 │ │Heap is FULL!! │ └────────────────┘ ``` 按下 INT1 ``` ┌────────────────┐ │ 2 │ │ 3 3 │ │ 7 4 4 │ │Pop 1. │ └────────────────┘ ``` 按下 INT1 ``` ┌────────────────┐ │ 3 │ │ 4 3 │ │ 7 4 │ │Pop 2. │ └────────────────┘ ``` 按下 INT1 ``` ┌────────────────┐ │ 3 │ │ 4 4 │ │ 7 │ │Pop 3. │ └────────────────┘ ``` ::: ### 第一題 如果沒要用EINT0的話就要刪掉 不然會出bug 讀取輸入EINT0與keypad #### 有extint的版本 main外面 ```cpp= int tmpInt,Intk; void EINT1_IRQHandler(void) { GPIO_CLR_INT_FLAG(PB, BIT15); // Clear GPIO interrupt flag Intk=1; } void Init_EXTINT(void) { // Configure EINT1 pin and enable interrupt by rising and falling edge trigger GPIO_SetMode(PB, BIT15, GPIO_MODE_INPUT); GPIO_EnableEINT1(PB, 15, GPIO_INT_RISING); // RISING, FALLING, BOTH_EDGE, HIGH, LOW NVIC_EnableIRQ(EINT1_IRQn); // Enable interrupt de-bounce function and select de-bounce sampling cycle time GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_64); GPIO_ENABLE_DEBOUNCE(PB, BIT15); } ``` main裡面 ```cpp= int k,tmpK,num; SYS_Init(); Init_EXTINT(); init_LCD(); clear_LCD(); OpenKeyPad(); // initialize 3x3 keypad while(1) { k=ScanKey(); CLK_SysTickDelay(50000); if ((k != tmpK && k !=0) || (Intk != tmpInt && Intk !=0)){ if(Intk)num=0; else num=k; /* * code * */ } tmpK=k; tmpInt=Intk; Intk=0; } ``` #### 無extint的版本 ```cpp= uint16_t k,tmpK,num; SYS_Init(); init_LCD(); clear_LCD(); OpenKeyPad(); while(1) { k=ScanKey(); CLK_SysTickDelay(50000); if (k != tmpK && k !=0){ num=k; /* * code * */ } tmpK=k; } ``` 完整程式 ```cpp= // // LCD_keypad : 3x3 keypad input and display on LCD // // EVB : Nu-LB-NUC140 // MCU : NUC140VE3CN (LQPF-100) #include <stdio.h> #include <stdlib.h> #include <string.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Scankey.h" int tmpInt,Intk; void EINT1_IRQHandler(void) { GPIO_CLR_INT_FLAG(PB, BIT15); // Clear GPIO interrupt flag Intk=1; } void Init_EXTINT(void) { // Configure EINT1 pin and enable interrupt by rising and falling edge trigger GPIO_SetMode(PB, BIT15, GPIO_MODE_INPUT); GPIO_EnableEINT1(PB, 15, GPIO_INT_RISING); // RISING, FALLING, BOTH_EDGE, HIGH, LOW NVIC_EnableIRQ(EINT1_IRQn); // Enable interrupt de-bounce function and select de-bounce sampling cycle time GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_64); GPIO_ENABLE_DEBOUNCE(PB, BIT15); } int isValidIPv4(const char *ip) { int num, dots = 0; char *ptr; // ????,?? strtok ???? char ipCopy[16]; strncpy(ipCopy, ip, sizeof(ipCopy) - 1); ipCopy[sizeof(ipCopy) - 1] = '\0'; // ????? NULL ?? // ?? strtok ???? ptr = strtok(ipCopy, "."); while (ptr != NULL) { // ???????? num = atoi(ptr); // ???????????? if (num < 0 || num > 255) { return 0; // ?????? } dots++; ptr = strtok(NULL, "."); } // ????????? return dots == 4; } void debug(){ PC13=0; CLK_SysTickDelay(50000); PC13=1; } int main(void) { char str[16]="\0"; int k,tmpK,tail=0,num,flag=0; SYS_Init(); Init_EXTINT(); init_LCD(); clear_LCD(); OpenKeyPad(); // initialize 3x3 keypad print_Line(0,"IPv4 Checker"); // print title while(1) { k=ScanKey(); CLK_SysTickDelay(50000); if ((k != tmpK && k !=0) || (Intk != tmpInt && Intk !=0)){ //debug(); //if(k==1)PC13=!PC13; if(Intk)num=0; else num=k; tail=strlen(str); if(tail<15){ str[tail++]=num+'0'; str[tail]='\0'; }else{ //flag=1; memset(str, 0, sizeof(str)); print_Line(2, " "); print_Line(3, " "); tail=0; str[tail++]=num+'0'; str[tail]='\0'; } if(tail<15 &&tail!=0 && tail%4==3){ str[tail++]='.'; str[tail]='\0'; } /*if(flag){ memset(str, 0, sizeof(str)); flag=0; }*/ } if(strlen(str)==15){ //PC14=0; if(isValidIPv4(str)){ print_Line(3, "OK"); }else{ print_Line(3, "ERROR"); } } if(strlen(str)!=0)print_Line(2, str); tmpK=k; tmpInt=Intk; Intk=0; } } ``` ### 第二題 畫面有 16行 4列 每個節點印出的位置如下圖 ![IMG_02956CDF7B66-1](https://hackmd.io/_uploads/BypHBuSlyx.jpg) ```cpp= #include <stdio.h> #include <stdlib.h> #include <string.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Scankey.h" #define MAX_SIZE 7 int heap[MAX_SIZE]; int size = 0; int tmpInt,Intk; void EINT1_IRQHandler(void) { GPIO_CLR_INT_FLAG(PB, BIT15); // Clear GPIO interrupt flag Intk=1; } void Init_EXTINT(void) { // Configure EINT1 pin and enable interrupt by rising and falling edge trigger GPIO_SetMode(PB, BIT15, GPIO_MODE_INPUT); GPIO_EnableEINT1(PB, 15, GPIO_INT_RISING); // RISING, FALLING, BOTH_EDGE, HIGH, LOW NVIC_EnableIRQ(EINT1_IRQn); // Enable interrupt de-bounce function and select de-bounce sampling cycle time GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_64); GPIO_ENABLE_DEBOUNCE(PB, BIT15); } void debug(){ PC13=0; CLK_SysTickDelay(50000); PC13=1; } void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } void insert(int value) { int current; if (size >= MAX_SIZE) { printf("Heap is full!\n"); return; } heap[size] = value; current = size; size++; while (current > 0) { int parent = (current - 1) / 2; if (heap[current] < heap[parent]) { swap(&heap[current], &heap[parent]); current = parent; } else { break; } } } int extractMin() { int min,current; if (size <= 0) { return -1; } min = heap[0]; heap[0] = heap[size - 1]; size--; current = 0; while (1) { int left = 2 * current + 1; int right = 2 * current + 2; int smallest = current; if (left < size && heap[left] < heap[smallest]) { smallest = left; } if (right < size && heap[right] < heap[smallest]) { smallest = right; } if (smallest != current) { swap(&heap[current], &heap[smallest]); current = smallest; } else { break; } } return min; } void printMap(){ int i=0; for(;i<4;i++){ print_Line(i, " "); } if(size!=0){ for(i =0;i< size;i++){ if(i==0){ printC(6*8,0,heap[i]+'0');//行(8 pixel) 列(16 pixel) }else if(i==1){ printC(2*8,16,heap[i]+'0'); }else if(i==2){ printC(10*8,16,heap[i]+'0'); }else if(i==3){ printC(0*8,32,heap[i]+'0'); }else if(i==4){ printC(4*8,32,heap[i]+'0'); }else if(i==5){ printC(8*8,32,heap[i]+'0'); }else if(i==6){ printC(12*8,32,heap[i]+'0'); } } } } int main() { int k,tmpK,num,tmp; SYS_Init(); Init_EXTINT(); init_LCD(); clear_LCD(); OpenKeyPad(); // initialize 3x3 keypad print_Line(2, "Hello World!!"); print_Line(3, "from LCD_minHeap"); while(1) { k=ScanKey(); CLK_SysTickDelay(50000); if ((k != tmpK && k !=0) || (Intk != tmpInt && Intk !=0)){ if(Intk)num=0; else { num=k; } if(num==0){ tmp=extractMin(); printMap(); if(size>0){ print_Line(3, "pop "); printC(7*8,48,tmp+'0'); }else{ print_Line(3, "Heap is EMPTY!!"); } }else{ if(size>=MAX_SIZE){ print_Line(3, "Heap is FULL!!"); }else{ insert(num); printMap(); print_Line(3, "Push "); printC(7*8,48,num+'0'); } } } tmpK=k; tmpInt=Intk; Intk=0; } return 0; } ``` ## 10/25 Image on LCD :::spoiler # Lab 5 - Image on LCD ## 題目 1 - 飲料販賣機 ![kirby_vending_mouth](https://hackmd.io/_uploads/Bk6FBNPxkx.jpg) 販賣機裡面販賣數種飲料,以下是這台販賣機有提供的飲料: | 中文名 | 英文名 | 價格 | |---------|-------------|---------| | 綠茶 | Green Tea | 20 | | 紅茶 | Black Tea | 25 | | 奶茶 | Milk Tea | 30 | | 咖啡 | Coffee | 30 | | 蘋果汁 | Apple Juice | 45 | - 在選單中第 0 行顯示 "Select Product"; 1~3 行顯示商品英文名稱與價格,每行第 0 個位置預留給游標、2~12 放商品名稱、14~15 價格。 - 游標的符號為 `>` ,按下 U/D 游標往上/下,若游標離開畫面範圍則捲動清單(不會動到第 0 行)使游標出現。 - 按下 5/10 表示投硬金額。 - 在選購商品的時候按下 R 鍵退還所有金額,在 LCD 第 2 行顯示 `Refund: x`,經過 3 秒後回到選購清單。 - 在選購商品的時候按下 B 鍵: - 若金額足夠清空畫面後在第 1 行顯示 `Thank You!!`、第 2 行顯示找零 `Refund: x`、投入金額歸 0,經過 3 秒後回到選單畫面。 - 若金額不夠在第 1 行顯示 `Not Enought :(`,經過 3 秒後回到選單畫面。投入金額不會重置。 - 目前的投幣金額要顯示在七段顯示器上。 ### 按鍵配置 ``` ┌───┬───┬───┐ │ U │ │ D │ ├───┼───┼───┤ │ 5 │ │ 10│ ├───┼───┼───┤ │ B │ │ R │ └───┴───┴───┘ ``` ### 範例 #### case 1 - 螢幕捲動 初始狀態 ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 D ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │ Green Tea 20│ │> Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 D ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │ Green Tea 20│ │ Black Tea 25│ │> Milk Tea 30│ └────────────────┘ ``` 按下 D ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │ Black Tea 25│ │ Milk Tea 30│ │> Coffee 30│ └────────────────┘ ``` 按下 U ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │ Black Tea 25│ │> Milk Tea 30│ │ Coffee 30│ └────────────────┘ ``` 按下 U ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │> Black Tea 25│ │ Milk Tea 30│ │ Coffee 30│ └────────────────┘ ``` 按下 U ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` #### case 2 - 金額不足 初始狀態 ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 5 ``` 7-seg: " 5" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 B ``` 7-seg: " 5" ┌────────────────┐ │ │ │Not Enought :( │ │ │ │ │ └────────────────┘ ``` 等候 3 秒 ``` 7-seg: " 5" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` #### case 3 - 購買成功 初始狀態 ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 5 ``` 7-seg: " 5" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 10 ``` 7-seg: " 15" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 10 ``` 7-seg: " 25" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 B ``` 7-seg: " 0" ┌────────────────┐ │ │ │Thank You!! │ │Refund: 5 │ │ │ └────────────────┘ ``` 等待 3 秒 ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` #### case 4 - 取消購買 初始狀態 ``` 7-seg: " 0" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 5 ``` 7-seg: " 5" ┌────────────────┐ │Select Product │ │> Green Tea 20│ │ Black Tea 25│ │ Milk Tea 30│ └────────────────┘ ``` 按下 R ``` 7-seg: " 0" ┌────────────────┐ │ │ │ │ │Refund: 5 │ │ │ └────────────────┘ ``` ## 題目 2 - 影像變形 ![img_tramsform](https://hackmd.io/_uploads/rkovlgUgJx.png) - 下載兩個角色的 bmp 檔,兩個檔案的大小皆為 64\*64 pixel。 - 顯示圖片在 LCD 的正中間。 - 按下 H 鍵,將目前 LCD 上的畫面水平翻轉。 - 按下 V 鍵,將目前 LCD 上的畫面垂直翻轉。 - 按下 L 鍵,將目前 LCD 上的畫面向左旋轉 90 度。 - 按下 R 鍵,將目前 LCD 上的畫面向右旋轉 90 度。 - 按下 C 鍵,將另一個角色的原始 bitmap 顯示在 LCD 上。 - 按下 INT1 ,載入目前角色原圖到 LCD 上。 ### bmp 檔 #### kirby_wiiDX_64x64.bmp ![kirby_wiiDX_64x64](https://hackmd.io/_uploads/Sy0J8lLlyl.bmp) #### bandee_wiiDX_64x64.bmp ![bandee_wiiDX_64x64](https://hackmd.io/_uploads/Hy-9nyLe1e.bmp) ### 按鍵配置 ``` ┌───┬───┬───┐ │ │ V │ │ ├───┼───┼───┤ │ H │ C │ R │ ├───┼───┼───┤ │ │ L │ │ └───┴───┴───┘ ``` ::: ### 第一題 ```cpp= #include <stdio.h> #include <stdlib.h> #include <string.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Scankey.h" #define up 1 #define down 2 #define stop 0 int pos=0,Mtop=0,Mbot=2; int price[5]={20,25,30,30,45}; char manu[5][17]={ " Green Tea 20", " Black Tea 25", " Milk Tea 30", " Coffee 30", " Apple Juice 45" }; void Display_7seg(int value) { int digit, highDigit=0, i; if (value > 0) { // ????????? if (value / 1000 != 0) { highDigit = 3; } else if (value / 100 != 0) { highDigit = 2; } else if (value / 10 != 0) { highDigit = 1; } else { highDigit = 0; } } // ????? digit = value / 1000; if (3 < highDigit + 1) { CloseSevenSegment(); // ????? ShowSevenSegment(3, digit); // ????? CLK_SysTickDelay(5000); // ?? 5000 ?? } // ????? value = value - digit * 1000; digit = value / 100; if (2 < highDigit + 1) { CloseSevenSegment(); ShowSevenSegment(2, digit); // ????? CLK_SysTickDelay(5000); } // ????? value = value - digit * 100; digit = value / 10; if (1 < highDigit + 1) { CloseSevenSegment(); ShowSevenSegment(1, digit); // ????? CLK_SysTickDelay(5000); } // ????? value = value - digit * 10; digit = value; CloseSevenSegment(); ShowSevenSegment(0, digit); // ????? CLK_SysTickDelay(5000); } void debug(){ PC13=0; CLK_SysTickDelay(50000); PC13=1; } void showP(int move){ int i; if(move==up){ if(pos>0){ pos--; }else{ if(Mtop>0){ Mtop--; Mbot--; } } }else if(move==down){ if(pos<2){ pos++; }else{ if(Mbot<4){ Mtop++; Mbot++; } } } print_Line(0,"Select Product "); for(i=0;i<3;i++){ print_Line(i+1,manu[Mtop+i]); } printC(0,16*(pos+1),'>'); } int main() { int k,tmpK,num,tmp,i,objPrice,money=0; char refund[16]; SYS_Init(); init_LCD(); clear_LCD(); OpenKeyPad(); // initialize 3x3 keypad showP(0); while(1){ k=ScanKey(); CLK_SysTickDelay(5000); if (k != tmpK && k !=0){ if(k==1){ showP(up); }else if(k==3){ showP(down); }else if(k==4){ money+=5; }else if(k==6){ money+=10; }else if(k==7){ objPrice=price[pos+Mtop]; if(money<objPrice){ print_Line(0, " "); print_Line(1, " "); print_Line(2, "Not Enought :( "); print_Line(3, " "); CLK_SysTickDelay(1000000); CLK_SysTickDelay(1000000); showP(0); }else{ Display_7seg(0); memset(refund,' ',sizeof(refund)); sprintf(refund,"Refund:"); sprintf(refund+strlen(refund),"%d ",money-objPrice); print_Line(0, " "); print_Line(1, "Thank You!! "); print_Line(2, " "); print_Line(2, refund); print_Line(3, " "); CLK_SysTickDelay(1000000); CLK_SysTickDelay(1000000); showP(0); money=0; } }else if(k==9){ Display_7seg(0); memset(refund,' ',sizeof(refund)); sprintf(refund,"Refund:"); sprintf(refund+strlen(refund),"%d ",money); print_Line(0, " "); print_Line(1, " "); print_Line(2, " "); print_Line(2, refund); print_Line(3, " "); CLK_SysTickDelay(1000000); CLK_SysTickDelay(1000000); showP(0); money=0; } } Display_7seg(money); tmpK=k; } return 0; } ``` ### 第二題 重點注意,這處理機的空間有夠有夠機車的小 用int陣列很有可能會超過裡面的大小 在函式裡面放陣列也非常有可能超過大小,要放全域可以用的空間會比較大 輸出前一定要用clear_LCD\(\)先清空畫面 FG_COLOR是有顏色 BG_COLOR是沒有顏色 ```cpp= #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Scankey.h" #define SIZE 64 //#define int int8_t int tmpInt,Intk; bool matrix[SIZE][SIZE] = {0}; bool temp[SIZE][SIZE] = {0};//微處理機太爛要用全域 void EINT1_IRQHandler(void) { GPIO_CLR_INT_FLAG(PB, BIT15); // Clear GPIO interrupt flag Intk=1; } void Init_EXTINT(void) { // Configure EINT1 pin and enable interrupt by rising and falling edge trigger GPIO_SetMode(PB, BIT15, GPIO_MODE_INPUT); GPIO_EnableEINT1(PB, 15, GPIO_INT_RISING); // RISING, FALLING, BOTH_EDGE, HIGH, LOW NVIC_EnableIRQ(EINT1_IRQn); // Enable interrupt de-bounce function and select de-bounce sampling cycle time GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_64); GPIO_ENABLE_DEBOUNCE(PB, BIT15); } /* 123 456 789 369 258 157 */ unsigned char picture1[64*8] = { // TCFST Logo 0x00,0x00,0x00,0x00,0xC0,0x30,0x08,0x04,0x02,0x02,0x01,0x01,0x01,0x01,0x01,0x02,0x04,0x0C,0xB0,0x40,0x40,0x40,0x20,0x20,0x20,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x20,0x20,0x20,0x40,0x40,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x80,0x60,0x20,0x20,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x10,0x10,0x30,0xE0,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x02,0x04,0x08,0x18,0x28,0xC8,0x08,0x08,0x08,0x08,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x03,0x1E,0x70,0x80,0x00,0xF0,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFC,0xF8,0xF8,0xFC,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFC,0xFC,0xFC,0xFF,0x7F,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x14,0x0A,0x14,0x0A,0x04,0x00,0x01,0x03,0x07,0x03,0x01,0x30,0x48,0x88,0x08,0x08,0x88,0x78,0x01,0x03,0x03,0x01,0x00,0x00,0x02,0x05,0x02,0x05,0x0A,0x85,0x82,0x80,0x80,0xC0,0xC0,0xC0,0xF0,0xBF,0x08,0x0C,0x06,0x03,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0E,0x30,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0xC0,0xE0,0xF0,0xF8,0xFC,0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFE,0xFC,0xF8,0xF0,0xE0,0xE0,0xC0,0xC0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x1F,0x0F,0x07,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF,0x07,0x1F,0x1F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x1F,0x1F,0x0F,0x0F,0x07,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x3F,0x7F,0x7F,0xFF,0xFF,0xFF,0xFF,0x7F,0x7F,0x3F,0x1F,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; unsigned char picture2[64*8] = { // TCFST Logo 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xC0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0xE0,0xE0,0xF0,0xF4,0xF4,0xF6,0xF7,0xF7,0xEF,0xE0,0xE0,0xE0,0xE0,0xC0,0xC0,0xBC,0xBF,0x7F,0xFF,0xF7,0x0F,0xFF,0xFE,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0xF8,0x06,0xF1,0x0F,0x83,0x00,0x00,0xEF,0x18,0x00,0x00,0x00,0x00,0xF0,0x1C,0x04,0x02,0x02,0x82,0xBA,0xFE,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,0x7D,0x7B,0x7B,0x60,0xB0,0xB0,0x70,0x70,0xE0,0xE0,0xC0,0x80,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x00,0x03,0x0F,0x3C,0xE7,0xFC,0x40,0x7F,0x60,0xE0,0xF0,0xF8,0xFC,0x7F,0x1F,0x8E,0xC6,0x22,0x19,0x06,0x81,0xF1,0xFC,0xE2,0x63,0x1F,0x03,0x03,0x03,0xE7,0x17,0x0F,0x9F,0x7F,0x0F,0x1F,0x7F,0x9F,0x1F,0x1F,0x1F,0x7F,0x0F,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC3,0x7F,0x3E,0x3F,0x3F,0x3F,0x3F,0x3E,0x18 , 0x00,0x00,0x00,0xC0,0x30,0x8C,0x42,0x22,0x13,0x0A,0x06,0x8C,0x98,0x68,0x07,0x03,0x1F,0xE0,0x0E,0x31,0x40,0x85,0x0A,0x04,0x01,0x03,0x03,0x01,0x00,0x00,0x00,0x00,0x1F,0x1F,0x9F,0x47,0x81,0x40,0x80,0x80,0x40,0x47,0x4C,0x70,0x20,0x20,0x6C,0xD0,0x90,0xE0,0x20,0x10,0x18,0x08,0x07,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x0C,0x07,0x06,0x05,0x04,0x06,0x02,0x03,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x7C,0x88,0x11,0x22,0x42,0x44,0x88,0x88,0x10,0x10,0x20,0x20,0xE0,0x30,0x08,0x05,0x02,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xC1,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x18,0x60,0x80,0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x87,0x6C,0x38,0x10,0x10,0x10,0x10,0x10,0x10,0x08,0x08,0x08,0x04,0x04,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 , 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x02,0x02,0x02,0x02,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; unsigned char using_picture[64*8]; void swap(bool* a,bool* b){ bool tmp = *a; *a = *b; *b = tmp; } void Flip_vertically(int row,int col,bool* matrix){ int i,j; for(i=0;i<row/2;i++){ for(j=0;j<col;j++){ swap( ( matrix + i * col + j ) , ( matrix + ( row - 1 -i) * col + j )); } } } void Flip_horizontally(int row,int col,bool* matrix){ int i,j; for(i=0;i<row;i++){ for(j=0;j<col/2;j++){ swap( ( matrix + i * col + j ) , ( matrix + i * col + (col - 1 - j )) ); } } } void Rotate_90_clockwise(int row, int col, bool matrix[64][64]) { int i,j; for ( i = 0; i < row; i++) { for ( j = 0; j < col; j++) { temp[j][63 - i ] = matrix[i][j]; } } for ( i = 0; i < col; i++) { for ( j = 0; j < row; j++) { matrix[i][j] = temp[i][j]; } } } void Rotate_90_counterclockwise(int row, int col, bool matrix[64][64]) { int i ,j; for ( i = 0; i < row; i++) { for ( j = 0; j < col; j++) { temp[col - j - 1][i] = matrix[i][j]; } } for ( i = 0; i < col; i++) { for ( j = 0; j < row; j++) { matrix[i][j] = temp[i][j]; } } } //自定義角度 void rotate_image(bool matrix[SIZE][SIZE], double angle) { // 角度轉弧度 double radian = angle * M_PI / 180.0; bool rotated[SIZE][SIZE]; // 計算圖像中心 int center_x = SIZE/2, center_y = SIZE/2; for (int y = 0; y < SIZE; y++) { for (int x = 0; x < SIZE; x++) { // 平移到中心 int translated_x = x - center_x; int translated_y = y - center_y; // 旋轉變換 int new_x = round(translated_x * cos(radian) - translated_y * sin(radian) + center_x); int new_y = round(translated_x * sin(radian) + translated_y * cos(radian) + center_y); // 檢查邊界 if (new_x >= 0 && new_x < SIZE && new_y >= 0 && new_y < SIZE) { rotated[y][x] = matrix[new_y][new_x]; } else { rotated[y][x] = 0; } } } for(int i=0;i<SIZE;i++){ for(int j=0;j<SIZE;j++){ matrix[i][j]=rotated[i][j]; } } } void hexToBinaryMatrix(unsigned char* BMP_Logo_TCFST, bool matrix[64][64]) { int i ,j ,k; for( i = 0; i < 8; i++) { for( j = 0; j < 64; j++) { for( k = 0; k < 8; k++) { matrix[i * 8 + k][j] = (BMP_Logo_TCFST[i * 64 + j] >> k) & 1; } } } } void binaryMatrixToHex(bool matrix[64][64], unsigned char* BMP_Logo_TCFST) { int i , j,k; unsigned char tmp; for( i = 0; i < 8; i++) { for( j = 0; j < 64; j++) { tmp = 0; for(k = 0; k <8 ; k++) { tmp = (tmp << 1) | matrix[i * 8 + (7 - k)][j]; } BMP_Logo_TCFST[i * 64 + j] = tmp; } } } void reset(unsigned char* p1,unsigned char* p2){ int i,j; for(i=0;i<8;i++){ for(j=0;j<64;j++){ *(p2+i*64+j)=*(p1+i*64+j); } } } void debug(){ PC13=0; CLK_SysTickDelay(50000); PC13=1; } signed main(void) { int i,j,k,tmpK,num,pflag=1;//pflag=1??picture1 pflag=0?picture2 SYS_Init(); Init_EXTINT(); init_LCD(); clear_LCD(); OpenKeyPad(); // initialize 3x3 keypad reset(picture1,using_picture); draw_Bmp64x64(31,0,FG_COLOR,BG_COLOR,using_picture); while(1){ k=ScanKey(); CLK_SysTickDelay(50000); if ((k != tmpK && k !=0) || (Intk != tmpInt && Intk !=0)){ debug(); if(Intk)num=0; else num=k; hexToBinaryMatrix(using_picture,matrix); if(num==2){ Flip_vertically(SIZE,SIZE,&matrix[0][0]); }else if(num==4){ Flip_horizontally(SIZE,SIZE,&matrix[0][0]); }else if(num==5){ pflag=(pflag+1)%2; if(pflag==1){ reset(picture1,using_picture); }else{ reset(picture2,using_picture); } hexToBinaryMatrix(using_picture,matrix); }else if(num==6){ Rotate_90_clockwise(SIZE,SIZE,matrix); }else if(num==8){ Rotate_90_counterclockwise(SIZE,SIZE,matrix); }if(num==0){ if(pflag==1){ reset(picture1,using_picture); }else{ reset(picture2,using_picture); } hexToBinaryMatrix(using_picture,matrix); } clear_LCD(); binaryMatrixToHex(matrix,using_picture); draw_Bmp64x64(31,0,FG_COLOR,BG_COLOR,using_picture); } tmpK=k; tmpInt=Intk; Intk=0; } return 0; } ``` ## 11/1 Draw on LCD :::spoiler # Lab 6 - Draw on LCD ## 題目 1 - 圓內接正多邊形 <!-- ![geogebra-export](https://hackmd.io/_uploads/rJARdyLeke.png) --> <iframe src="https://www.geogebra.org/calculator/wnw9bhen?embed" width="800" height="450" allowfullscreen style="border: 1px solid #e4e4e4;border-radius: 4px;" frameborder="0"></iframe> 參考上圖,請在圓內劃出內接正多邊形。 - 在 LCD 上中心畫上半徑 $r = 30$ pixels 的圓。 - n 表示內接多邊形的邊數,初始值為 6,範圍 3~12,利用按鍵增減;每按一次按鍵只會觸發一次。 - 在七段顯示器上顯示 n 的數值,輸出格式靠右、沒有前綴 0。 - 右方為 x 軸方向、上方為 y 軸方向;多邊形的第一個點在極座標 $(r, \frac{\pi}{2})$ 的位置。 註:正多邊形內不用著色。 ### 按鍵配置 ``` ┌───┬───┬───┐ │ │ │ │ ├───┼───┼───┤ │-1 │ │+1 │ ├───┼───┼───┤ │ │ │ │ └───┴───┴───┘ ``` ###### reference: `linear algebra` ## 題目 2 - 貪吃蛇(一) ![](https://www.coolmathgames.com/sites/default/files/Snake_OG-logo.jpg) - 在 LCD 上以 2 pixles 為一格;一個「蛇身」的大小是 1\*1 格。 - 初始狀態下,貪吃蛇在畫面的中間,長度為 16 格,頭朝左邊。 - 按下方向鍵後,若可以往該方向移動,每 0.5 秒往該方向移動 1 格;移動的過程中能透過按鍵改變方向。 - 以下任一條件符合應判定「不可移動」,此時蛇停止移動: - 移動後會超過邊界。 - 移動後會與其他蛇身重疊。 - 蛇停止後,按下可以移動的方向就會再次移動。 - 按鍵對應方向如下: | Key | dir | |-----|-------| | U | up | | D | down | | L | left | | R | right | ### 按鍵配置 ``` ┌───┬───┬───┐ │ │ U │ │ ├───┼───┼───┤ │ L │ │ R │ ├───┼───┼───┤ │ │ D │ │ └───┴───┴───┘ ``` ###### reference: `queue` ::: 這兩題不是我寫的 ### 第一題 :::danger 會微閃爍 所以要給好助教檢查才可以 ::: ```cpp= // // LCD_Graph2D : draw line, circle, triangle, rectangle onto LCD // // EVB : Nu-LB-NUC140 // MCU : NUC140VE3CN #include <stdio.h> #include <math.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Draw2D.h" #include "Scankey.h" #include "Seven_Segment.h" #define CENTER_X 63 // LCD center X coordinate #define CENTER_Y 31 // LCD center Y coordinate #define RADIUS 30 // Circle radius in pixels #define PI 3.14159265 int side = 6; void Display_7seg(uint16_t value) { uint8_t digit; if(value >= 10){ digit = value / 10; CloseSevenSegment(); ShowSevenSegment(1,digit); CLK_SysTickDelay(4000); value = value - digit * 10; } digit = value; CloseSevenSegment(); ShowSevenSegment(0,digit); CLK_SysTickDelay(4000); } void draw_polygon(int sides) { int x1, y1, x2, y2; int i; float angle = PI/2; // Starting from 90 degrees (p/2) float angleStep = 2*PI/sides; Display_7seg(side); // Calculate and draw each side of the polygon for(i = 0; i < sides; i++) { Display_7seg(side); // Calculate current point x1 = CENTER_X + RADIUS * cos(angle); y1 = CENTER_Y - RADIUS * sin(angle); // Subtract because Y axis is inverted // Calculate next point x2 = CENTER_X + RADIUS * cos(angle + angleStep); y2 = CENTER_Y - RADIUS * sin(angle + angleStep); // Draw line between points draw_Line(x1, y1, x2, y2, FG_COLOR, BG_COLOR); Display_7seg(side); angle += angleStep; } Display_7seg(side); } int main(void) { int i; int key = 0; int flag = 1; SYS_Init(); OpenKeyPad(); OpenSevenSegment(); init_LCD(); clear_LCD(); //draw_Circle(63,30,20,FG_COLOR, BG_COLOR); // draw a circle Display_7seg(side); while(1){ draw_Circle(63,31,30,FG_COLOR, BG_COLOR); key = ScanKey(); if(key != 0 && flag == 1){ clear_LCD(); flag = 0; //-1 if(key == 4 && side > 3){ side--; } //+1 else if(key == 6 && side < 12){ side++; } } else if(key == 0 && flag == 0){ flag = 1; } draw_polygon(side); Display_7seg(side); } } ``` ### 第二題 ```cpp= // // LCD_Graph_Pingpong: draw a circle bouncing around between two bars on LCD // // EVB : Nu-LB-NUC140 // MCU : NUC140VE3CN // #include <stdio.h> #include <math.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Draw2D.h" #include "Scankey.h" #define PIXEL_ON 1 #define PIXEL_OFF 0 int32_t main (void) { int snake[16][2]={ {64,32},{66,32},{68,32},{70,32}, {72,32},{74,32},{76,32},{78,32}, {80,32},{82,32},{84,32},{86,32}, {88,32},{90,32},{92,32},{94,32} }; int dirX, dirY; int i,touch=0,stop=0; uint16_t key; uint16_t x, y; uint16_t fgColor, bgColor; SYS_Init(); init_LCD(); clear_LCD(); OpenKeyPad(); x = snake[0][0]; y = snake[0][1]; dirX = -1; // x direction dirY = 0; // y direction bgColor = BG_COLOR; while(1) { touch=0; if((key=ScanKey())){ switch(key){ case 2: dirX=0; dirY=-1; break; case 4: dirX=-1; dirY=0; break; case 6: dirX=1; dirY=0; break; case 8: dirX=0; dirY=1; break; } } x = snake[0][0]; y = snake[0][1]; x = x + dirX * 2; y = y + dirY * 2; if((x-1) < 0 || (x+2) > 127 || (y-1) < 0 || (y+2) > 63) touch=1; for(i=0;i<15;i++){ if(x==snake[i][0] && y==snake[i][1]) touch=1; } if(touch==1) stop=1; else stop=0; fgColor = FG_COLOR; for(i=0;i<16;i++){ x = snake[i][0]; y = snake[i][1]; draw_Rectangle(x, y, x+1, y+1, fgColor, bgColor); } CLK_SysTickDelay(500000); // adjustable delay for vision fgColor = BG_COLOR; for(i=0;i<16;i++){ x = snake[i][0]; y = snake[i][1]; draw_Rectangle(x, y, x+1, y+1, fgColor, bgColor); } if(stop==0){ for(i=15;i>0;i--){ snake[i][0] = snake[i-1][0]; snake[i][1] = snake[i-1][1]; } snake[0][0] = snake[0][0] + dirX * 2; snake[0][1] = snake[0][1] + dirY * 2; } } } ``` ## 11/15 Draw on LCD :::spoiler # Lab 7 - LCD Review ## 題目 1 - 貪吃蛇 (二) ![](https://www.coolmathgames.com/sites/default/files/Snake_OG-logo.jpg) 在 lab 6-2 的基礎下增加「吃食物變長」的機制。 - 在畫面上隨機產生「食物」 - 食物的大小為 1\*1 格。 - 當蛇頭碰到食物,蛇身增加 1 格,並在其他隨機位置產生一個新的食物。 - 產生食物的位置不可與蛇身重疊。 - 其他要求與 [lab 6-2](https://hackmd.io/@y9xPfshrTTCAiAx61Re4DQ/ryPdGV8gye#%E9%A1%8C%E7%9B%AE-2---%E8%B2%AA%E5%90%83%E8%9B%87%EF%BC%88%E4%B8%80%EF%BC%89) 相同。 ### 按鍵配置 ``` ┌───┬───┬───┐ │ │ U │ │ ├───┼───┼───┤ │ L │ │ R │ ├───┼───┼───┤ │ │ D │ │ └───┴───┴───┘ ``` ### 偽隨機數程式碼參考 ```clike int main(...){ int __count; ... while(true){ srand(__count++); ... if(...){ x = rand(); } ... } } ``` ###### reference: `queue`, `linked list` ## 【加分題】題目 2 - 迷宮設計 ![maze-2a](https://hackmd.io/_uploads/HkzieSuZ1g.png) 請設計 64 個不同的迷宮。 - 迷宮關卡設計 - 一格大小為 8\*8 pixel,迷宮的大小為 8\*16 格;繪製出所有 16 種格子造型。 - 第 0~62 關的地圖請至 [GitHub](https://github.com/Jacky924815/maze-bmp-generator) 下載 `output/maze_array.h`;<font style="background-color:#3F3F46">當然,你也可以自己設計這 63 關</font>。 - 自己設計第 63 關的迷宮,需要把「自己的序號」融入迷宮的地形。 - 利用格子拼湊出各關的迷宮。 - 在七段顯示器上顯示目前的關卡編號。 - 利用按鍵跳轉關卡,增減的數字見鍵位配置,要可以循環。 P.S. 本題的靈感來自於[櫻井政博的遊戲開發經驗分享](https://youtu.be/ZUY2AtBD6Sk?si=kVmhDZceOlVjS0Qi&t=249)。 P.P.S. *Super Mario Bros.* 的遊戲容量為 31 KB。 ### 按鍵配置 ``` ┌───┬───┬───┐ │ │+10│ │ ├───┼───┼───┤ │ -1│ │ +1│ ├───┼───┼───┤ │ │-10│ │ └───┴───┴───┘ ``` ### 資料格式 當你執行完 `maze_gen.py` 後,你會得到 BMP 與 txt 各 62 張,編號一樣的是成對的。 #### BMP BMP 的大小為 64\*128 pixel,位元深度 1。 ![maze-1](https://hackmd.io/_uploads/BkP9slF-ke.bmp =50%x) 這個 BMP 檔是為了方便看對應的 txt 檔描述的迷宮樣子。 #### txt txt 存了 8 個 16 位數的十六進位數,位數代表該格與隔壁的連接關係。 ``` B900A9290000A929 CE9AFDAD82335EBD 6DC6DCEFF3BB3FDC 04E9E56FF9C6377D 88E7D80ED4CA3B94 CE50CC84CAF58EF9 6F98EF73DC6B5CCC 2567540254271467 ``` 每個位數的 bit0~bit3 對應左、右、上、下的連通狀況,若該位元為 '1' 表示該方向有連通,例如 `B` 與左、右、下有連通。 ###### reference: `data compression` ::: ### 第一題 ```cpp= #include <stdio.h> #include <stdlib.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Draw2D.h" #include "Scankey.h" #define PIXEL_ON 1 #define PIXEL_OFF 0 int32_t main (void) { int snake[100][2] = { {64,32},{66,32},{68,32},{70,32}, {72,32},{74,32},{76,32},{78,32}, {80,32},{82,32},{84,32},{86,32}, {88,32},{90,32},{92,32},{94,32} }; int dirX = -1, dirY = 0; int length = 16; // ?????? int i, touch = 0, stop = 0; uint16_t key; uint16_t x, y; uint16_t fgColor, bgColor; int foodX, foodY; SYS_Init(); init_LCD(); clear_LCD(); OpenKeyPad(); // ??????? x = snake[0][0]; y = snake[0][1]; // ???????????,???????? foodX = 4 + (rand() % 60) * 2; // X???? 4 ? 124(128-4) foodY = 4 + (rand() % 30) * 2; // Y???? 4 ? 60(64-4) bgColor = BG_COLOR; while(1) { touch = 0; // ?????? if ((key = ScanKey())) { switch(key) { case 2: dirX = 0; dirY = -1; break; case 4: dirX = -1; dirY = 0; break; case 6: dirX = 1; dirY = 0; break; case 8: dirX = 0; dirY = 1; break; } } // ???????? x = snake[0][0] + dirX * 2; y = snake[0][1] + dirY * 2; // ????????? if ((x-1) < 0 || (x+2) > 127 || (y-1) < 0 || (y+2) > 63) touch = 1; for(i = 0; i < length; i++) { if (x == snake[i][0] && y == snake[i][1]) touch = 1; } // ???????? if (touch == 1) stop = 1; else stop = 0; // ???? fgColor = FG_COLOR;//FG_COLOR是亮 for(i = 0; i < length; i++) { draw_Rectangle(snake[i][0], snake[i][1], snake[i][0]+1, snake[i][1]+1, fgColor, bgColor); } // ???? draw_Rectangle(foodX, foodY, foodX+1, foodY+1, FG_COLOR, bgColor); CLK_SysTickDelay(500000); // ????????? // ???? fgColor = BG_COLOR;//BG_COLOR是暗 for(i = 0; i < length; i++) { draw_Rectangle(snake[i][0], snake[i][1], snake[i][0]+1, snake[i][1]+1, fgColor, bgColor); } // ?????? if (stop == 0) { for(i = length - 1; i > 0; i--) { snake[i][0] = snake[i - 1][0]; snake[i][1] = snake[i - 1][1]; } snake[0][0] = x; snake[0][1] = y; // ???????? if (x == foodX && y == foodY) { length++; // ?????? // ????????,?????????? do { foodX = 4 + (rand() % 60) * 2; // X???? 4 ? 124 foodY = 4 + (rand() % 30) * 2; // Y???? 4 ? 60 touch = 0; for (i = 0; i < length; i++) { if (foodX == snake[i][0] && foodY == snake[i][1]) { touch = 1; break; } } } while (touch == 1); // ??????????????? } } } } ``` ### 第二題 ```cpp= #include <stdio.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Scankey.h" #include "Seven_Segment.h" #define ROW 8 #define COLUMN 16 // display an integer on four 7-segment LEDs void Display_7seg(uint16_t value) { uint8_t digit; digit = value / 1000; CloseSevenSegment(); ShowSevenSegment(3,digit); CLK_SysTickDelay(5000); value = value - digit * 1000; digit = value / 100; CloseSevenSegment(); ShowSevenSegment(2,digit); CLK_SysTickDelay(5000); value = value - digit * 100; digit = value / 10; CloseSevenSegment(); ShowSevenSegment(1,digit); CLK_SysTickDelay(5000); value = value - digit * 10; digit = value; CloseSevenSegment(); ShowSevenSegment(0,digit); CLK_SysTickDelay(5000); } typedef unsigned char byte; byte mazes[64][ROW][COLUMN];//這個是要從github上複製下來 int main(void) { char arr[16][8] = { {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //0 {0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF}, //1 {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81}, //2 {0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81}, //3 {0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF}, //4 {0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF}, //5 {0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81}, //6 {0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81}, //7 {0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF}, //8 {0x81, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF}, //9 {0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x81}, //10 {0x81, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x81}, //11 {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, //12 {0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, //13 {0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81}, //14 {0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81}, //15 }; uint8_t key; uint8_t unkey = 1; int x = 0; int y = 0; int i = 0; int j = 0; int level = 0; int last_level = 0; SYS_Init(); init_LCD(); clear_LCD(); OpenKeyPad(); // initialize 3x3 keypad x = 0; y = 0; while (y<64){ while(x<128){ draw_Bmp8x8(x, y, 1, 0, arr[mazes[level][y/8][x/8]]); x+=8; } x = 0; y+= 8; } while (1){ key = ScanKey(); if (unkey == 1 && key != 0){ switch (key){ case 2: level+=10; break; case 4: level-=1; break; case 6: level+=1; break; case 8: level-=10; break; } if (level > 63){ level-=64; } if (level < 0){ level+=64; } unkey = 0; } else if (unkey == 0 && key == 0){ unkey = 1; } Display_7seg(level); if (last_level != level){ clear_LCD(); x = 0; y = 0; while (y<64){ while(x<128){ draw_Bmp8x8(x, y, 1, 1, arr[mazes[level][y/8][x/8]]); x+=8; } x = 0; y+= 8; } } last_level = level; } } ``` ```cpp= #include <stdio.h> #include <math.h> #include <stdbool.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Draw2D.h" #include "Scankey.h" #define PIXEL_ON 1 #define PIXEL_OFF 0 #define ROW 8 #define COLUMN 16 /* 128 /16= 8 64 /8= 8 */ typedef unsigned char byte; byte mazes[64][ROW][COLUMN];//這個要從github下載 unsigned char blockMAP[16*8]={ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0xFF, 0xFF,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81, 0xFF,0x80,0x80,0x80,0x80,0x80,0x80,0xFF, 0x81,0x80,0x80,0x80,0x80,0x80,0x80,0xFF, 0xFF,0x80,0x80,0x80,0x80,0x80,0x80,0x81, 0x81,0x80,0x80,0x80,0x80,0x80,0x80,0x81, 0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0xFF, 0x81,0x01,0x01,0x01,0x01,0x01,0x01,0xFF, 0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0x81, 0x,0x,0x,0x,0x,0x,0x,0x, 0x,0x,0x,0x,0x,0x,0x,0x, 0x,0x,0x,0x,0x,0x,0x,0x, 0x,0x,0x,0x,0x,0x,0x,0x, 0x,0x,0x,0x,0x,0x,0x,0x }; /* 0000 0 0001 1 0010 2 0011 3 0100 4 0101 5 0110 6 0111 7 1000 8 1001 9 1010 A 1011 B 1100 C 1101 D 1110 E 1111 F */ void printRECT(unsigned char block,uint16_t y,uint16_t x){ bool left=0,right=0,up=0,down=0; if(block==0x00){ }else if(block==0x01){ }else if(block==0x02){ }else if(block==0x03){ }else if(block==0x04){ }else if(block==0x05){ }else if(block==0x06){ }else if(block==0x07){ }else if(block==0x08){ }else if(block==0x09){ }else if(block==0x0A){ }else if(block==0x0B){ }else if(block==0x0C){ }else if(block==0x0D){ }else if(block==0x0E){ }else if(block==0x0F){ } return ; } void printMAP(int cnt){ uint16_t i,j; for(i=0;i<8;i++){ for(j=0;j<16;j++){ printRECT(mazes[cnt][i][j],i*8,j*16); } } return; } int32_t main (void) { uint16_t k,tmpK,num,cnt=0; SYS_Init(); init_LCD(); clear_LCD(); OpenKeyPad(); while(1) { k=ScanKey(); CLK_SysTickDelay(50000); if (k != tmpK && k !=0){ num=k; if(num==6){ cnt=(cnt+1+62)%62; }else if(num==4){ cnt=(cnt-1+62)%62; }else if(num==2){ cnt=(cnt+10)%62; }else if(num==8){ cnt=(cnt-10)%62; } printMAP(cnt); } tmpK=k; } } ``` ## 11/22 ADC :::spoiler # Lab 8 ADC ## 題目 1 - 亮度可控七段顯示器 ![](https://upload.wikimedia.org/wikipedia/commons/3/34/Dutycycle.png) 在七段顯示器上顯示其 COM 端的平均電壓。 - 顯示格式到小數第三位,要顯示小數點。 - 利用 VR 控制亮度,最左邊為「亮度 0%」而最右邊為「亮度 100%」。 - 當 INT1 按下的時候,以「亮度 100%」顯示 COM 端的平均電壓,否則以該亮度顯示其數值。 P.S. 翻手冊找 COM 端是幾 V。 ### Hint 請利用「開」的時間比例達成目的,詳細參閱[維基百科 Duty Cycle 的說明](https://en.wikipedia.org/wiki/Duty_cycle)。 ## 題目 2 - 蹺蹺板 ![24153124_s source: https://zh-tw.photo-ac.com/photo/24153124](https://hackmd.io/_uploads/SJ8520WGkx.jpg =100%x) - 蹺蹺板的木板長度為 80 pixel - VR 置中時,翹翹板的木板呈水平位置;往左/右轉到底時木板偏移角度往左/右轉 20 度,旋轉軸心在木板正中間。 - 在七段顯示器賞顯示目前的偏移角度,顯示整數部分即可,右轉為正向,若有需要顯示負號。 ::: ![image](https://hackmd.io/_uploads/r1nx6aeEye.png) ### 第一題 Library要記得加 ![image](https://hackmd.io/_uploads/SyfBICazkx.png) ![image](https://hackmd.io/_uploads/SybzV06f1e.png) 這裡要改 MCU_init.h ```cpp= //Define Clock source #define MCU_CLOCK_SOURCE #define MCU_CLOCK_SOURCE_HXT // HXT, LXT, HIRC, LIRC #define MCU_CLOCK_FREQUENCY 50000000 //Hz //Define MCU Interfaces #define MCU_INTERFACE_ADC #define ADC_CLOCK_SOURCE_HXT // HXT, LXT, PLL, HIRC, HCLK #define ADC_CLOCK_DIVIDER 1 #define PIN_ADC7_PA7 #define ADC_CHANNEL_MASK ADC_CH_7_MASK #define ADC_INPUT_MODE ADC_INPUT_MODE_SINGLE_END // SINGLE_END, DIFFERENTIAL #define ADC_OPERATION_MODE ADC_OPERATION_MODE_CONTINUOUS // SINGLE, SINGLE_CYCLE, CONTINUOUS //Define MCU Interfaces #define MCU_INTERFACE_SPI3 #define SPI3_CLOCK_SOURCE_HCLK // HCLK, PLL #define PIN_SPI3_SS0_PD8 #define PIN_SPI3_SCLK_PD9 #define PIN_SPI3_MISO0_PD10 #define PIN_SPI3_MOSI0_PD11 ``` 主程式 ```cpp= #include <stdio.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "Scankey.h" #include "LCD.h" #include "Seven_Segment.h" volatile int KEY_Flag = -1; volatile int ADC_Value = 0xfff; void GPAB_IRQHandler(void) { if (PA->ISRC & BIT0) { //ISRC是表示哪個腳位有觸發 PA->ISRC |= BIT0; // clear interupt state; switch (ScanKey()) { case 3: KEY_Flag=3; break; case 6: KEY_Flag=6; break; case 9: KEY_Flag=9; break; } } else if (PA->ISRC & BIT1) { PA->ISRC |= BIT1; switch (ScanKey()) { case 2: KEY_Flag=2; break; case 5: KEY_Flag=5; break; case 8: KEY_Flag=8; break; } } else if (PA->ISRC & BIT2) { PA->ISRC |= BIT2; switch (ScanKey()) { case 1: KEY_Flag=1; break; case 4: KEY_Flag=4; break; case 7: KEY_Flag=7; break; } } else { PA->ISRC = PA->ISRC; // clear all GPA pins } //讓keypad掃不到 PA0 = PA1 = PA2 = 1; PA3 = PA4 = PA5 = 0; } void EINT1_IRQHandler(void) { GPIO_CLR_INT_FLAG(PB, BIT15); // Clear GPIO interrupt flag KEY_Flag=0; } void ADC_IRQHandler(void) { uint32_t u32Flag; u32Flag = ADC_GET_INT_FLAG(ADC, ADC_ADF_INT);//看看是不是adc有被觸發 if(u32Flag & ADC_ADF_INT) { ADC_Value = ADC_GET_CONVERSION_DATA(ADC, 7);//取adc的值 } ADC_CLR_INT_FLAG(ADC, u32Flag); } void Init_ADC(void) { ADC_Open(ADC, ADC_INPUT_MODE, ADC_OPERATION_MODE, ADC_CHANNEL_MASK); ADC_POWER_ON(ADC); ADC_EnableInt(ADC, ADC_ADF_INT); NVIC_EnableIRQ(ADC_IRQn); ADC_START_CONV(ADC); } void Init_GPIO() { //7-seg OpenSevenSegment(); // LCD init_LCD(); PD14=1;//點亮LCD背板 // ADC Init_ADC(); //keypad // PA 0-2 & 3-5: keypad OpenKeyPad(); //設定PA 3 4 5為輸出模式 GPIO_SetMode(PA, (BIT3 | BIT4 | BIT5),GPIO_MODE_OUTPUT);//這行不確定 要不要加 //設定PA 0 1 2為輸入輸出模式 GPIO_SetMode(PA, (BIT0 | BIT1 | BIT2),GPIO_MODE_QUASI);//這行不確定 要不要加 // enable Interupt 設定keypad的PA0 PA1 PA2是落下觸發 GPIO_EnableInt(PA, 0, GPIO_INT_FALLING); GPIO_EnableInt(PA, 1, GPIO_INT_FALLING); GPIO_EnableInt(PA, 2, GPIO_INT_FALLING); //允許PA可以進入中斷 NVIC_EnableIRQ(GPAB_IRQn); //設定debounce的時長頻率 GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_64); // debounce keypad GPIO_ENABLE_DEBOUNCE(PA, (BIT0 | BIT1 | BIT2)); //int1 // Configure EINT1 pin and enable interrupt by rising and falling edge trigger GPIO_SetMode(PB, BIT15, GPIO_MODE_INPUT); GPIO_EnableEINT1(PB, 15, GPIO_INT_BOTH_EDGE); NVIC_EnableIRQ(EINT1_IRQn); // Enable interrupt de-bounce function and select de-bounce sampling cycle time GPIO_ENABLE_DEBOUNCE(PB, BIT15); //讓keypad掃不到 PA0 = PA1 = PA2 = 1; PA3 = PA4 = PA5 = 0; } void showNumber (double num, uint32_t brightness) { const uint32_t gap = 2e3; int i; double percent; for(i=1; i<=4; i++) { percent = 1.0*brightness/0xfff; CloseSevenSegment(); CLK_SysTickDelay(gap*(1-percent)+1); PE1 = (i==1) ? 0 : 1; ShowSevenSegment(4-i, (int)num%10); CLK_SysTickDelay(gap*percent+1); num *= 10; } } int main(void) { int toggle=0; SYS_Init(); Init_GPIO(); clear_LCD(); while(1) { if (KEY_Flag == 0) { toggle = !toggle; KEY_Flag = -1; } showNumber(5.0*ADC_Value/0xfff, (toggle)?0xfff:ADC_Value); } } ``` ![image](https://hackmd.io/_uploads/S1owiaeEJg.png) ### 第二題 主程式 ```cpp= #include <stdio.h> #include <math.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Draw2D.h" #include "Seven_Segment.h" // ???? char Text[16]; int angleInt; #define PI 3.14159265359 #define SEESAW_LENGTH 80 // ????? #define CENTER_X 64 // LCD???X #define CENTER_Y 32 // LCD???Y // ??????? void drawSeesaw(int angle) { int x1, y1, x2, y2; double radians = angle * PI / 180.0; // ???????? uint16_t fgColor = FG_COLOR; // ??? uint16_t bgColor = BG_COLOR; // ??? // ?????????? x1 = CENTER_X - (SEESAW_LENGTH/2) * cos(radians); y1 = CENTER_Y - (SEESAW_LENGTH/2) * sin(radians); x2 = CENTER_X + (SEESAW_LENGTH/2) * cos(radians); y2 = CENTER_Y + (SEESAW_LENGTH/2) * sin(radians); // ???????? clear_LCD(); // ?????,??????? draw_Line(x1, y1, x2, y2, fgColor, bgColor); // ?????????????? draw_Rectangle(CENTER_X-1, CENTER_Y-1, CENTER_X+1, CENTER_Y+1, fgColor, bgColor); } void Display_7seg(int16_t value) { uint8_t digit; uint8_t negative = 0; if (value < 0) { negative = 1; value = -value; } if (value > 20) value = 20; if (negative) { CloseSevenSegment(); ShowSevenSegment(2, 16); CLK_SysTickDelay(5000); } digit = value / 10; if (digit > 0) { CloseSevenSegment(); ShowSevenSegment(1, digit); CLK_SysTickDelay(5000); } value = value - digit * 10; CloseSevenSegment(); ShowSevenSegment(0, value); CLK_SysTickDelay(5000); } void AngleMeasurement(int16_t ADCvalue) { double angle; int i; if (ADCvalue <= 2047) { angle = -20 + (20 * ADCvalue / 2047.0); } else { angle = (20 * (ADCvalue - 2048) / 2047.0); } angleInt = (int) angle; for(i=0;i<15;i++) Display_7seg(angleInt); // ????? drawSeesaw(angleInt); } void ADC_IRQHandler(void) { uint32_t u32Flag; int16_t ADCvalue; u32Flag = ADC_GET_INT_FLAG(ADC, ADC_ADF_INT); if(u32Flag & ADC_ADF_INT) { ADCvalue = ADC_GET_CONVERSION_DATA(ADC, 7); AngleMeasurement(ADCvalue); } ADC_CLR_INT_FLAG(ADC, u32Flag); } void Init_ADC(void) { ADC_Open(ADC, ADC_INPUT_MODE, ADC_OPERATION_MODE, ADC_CHANNEL_MASK); ADC_POWER_ON(ADC); ADC_EnableInt(ADC, ADC_ADF_INT); NVIC_EnableIRQ(ADC_IRQn); ADC_START_CONV(ADC); } int32_t main(void) { SYS_Init(); init_LCD(); clear_LCD(); Init_ADC(); OpenSevenSegment(); while(1) { // ???????,?????ADC???? } } ``` ![image](https://hackmd.io/_uploads/SybzV06f1e.png) 這裡要改 MCU_init.h ```cpp= //Define Clock source #define MCU_CLOCK_SOURCE #define MCU_CLOCK_SOURCE_HXT // HXT_ LXT, HIRC, LIRC #define MCU_CLOCK_FREQUENCY 50000000 //Hz //Define MCU Interfaces // LCD #define MCU_INTERFACE_SPI3 #define SPI3_CLOCK_SOURCE_HCLK // HCLK, PLL #define PIN_SPI3_SS0_PD8 #define PIN_SPI3_SCLK_PD9 #define PIN_SPI3_MISO0_PD10 #define PIN_SPI3_MOSI0_PD11 // ADC #define MCU_INTERFACE_ADC #define ADC_CLOCK_SOURCE_HXT // HXT, LXT, PLL, HIRC, HCLK #define ADC_CLOCK_DIVIDER 7 #define PIN_ADC7_PA7 #define ADC_CHANNEL_MASK ADC_CH_7_MASK #define ADC_INPUT_MODE ADC_INPUT_MODE_SINGLE_END // SINGLE_END, DIFFERENTIAL #define ADC_OPERATION_MODE ADC_OPERATION_MODE_CONTINUOUS // SINGLE, SINGLE_CYCLE, CONTINUOUS ``` 七段也要改 ```cpp= #include <stdio.h> #include "NUC100Series.h" #include "GPIO.h" #include "SYS.h" #include "Seven_Segment.h" #define SEG_N0 0x82 #define SEG_N1 0xEE #define SEG_N2 0x07 #define SEG_N3 0x46 #define SEG_N4 0x6A #define SEG_N5 0x52 #define SEG_N6 0x12 #define SEG_N7 0xE6 #define SEG_N8 0x02 #define SEG_N9 0x62 #define SEG_N10 0x22 #define SEG_N11 0x1A #define SEG_N12 0x93 #define SEG_N13 0x0E #define SEG_N14 0x13 #define SEG_N15 0x33 uint8_t SEG_BUF[16]={SEG_N0, SEG_N1, SEG_N2, SEG_N3, SEG_N4, SEG_N5, SEG_N6, SEG_N7, SEG_N8, SEG_N9, SEG_N10, SEG_N11, SEG_N12, SEG_N13, SEG_N14, SEG_N15}; void OpenSevenSegment(void) { GPIO_SetMode(PC, BIT4, GPIO_PMD_OUTPUT); GPIO_SetMode(PC, BIT5, GPIO_PMD_OUTPUT); GPIO_SetMode(PC, BIT6, GPIO_PMD_OUTPUT); GPIO_SetMode(PC, BIT7, GPIO_PMD_OUTPUT); PC4=0; PC5=0; PC6=0; PC7=0; GPIO_SetMode(PE, BIT0, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT1, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT2, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT3, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT4, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT5, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT6, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT7, GPIO_PMD_QUASI); PE0=0; PE1=0; PE2=0; PE3=0; PE4=0; PE5=0; PE6=0; PE7=0; } void ShowSevenSegment(uint8_t no, uint8_t number) { uint8_t temp,i; temp=SEG_BUF[number]; if(number==16){ PE0=1; PE1=1; PE2=1; PE3=1; PE4=1; PE5=1; PE6=1; PE7=0; }else{ for(i=0;i<8;i++) { if((temp&0x01)==0x01) switch(i) { case 0: PE0=1; break; case 1: PE1=1; break; case 2: PE2=1; break; case 3: PE3=1; break; case 4: PE4=1; break; case 5: PE5=1; break; case 6: PE6=1; break; case 7: PE7=1; break; } else switch(i) { case 0: PE0=0; break; case 1: PE1=0; break; case 2: PE2=0; break; case 3: PE3=0; break; case 4: PE4=0; break; case 5: PE5=0; break; case 6: PE6=0; break; case 7: PE7=0; break; } temp=temp>>1; } } switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void CloseSevenSegment(void) { PC4=0; PC5=0; PC6=0; PC7=0; } ``` ## 12/6 Timer :::spoiler # Lab 9 - Timer ## 題目 1 - 碼錶 ![](https://img.tw.my-best.com/product_images/1c04027c4568cef192c9134c0e8fa3b1.png?ixlib=rails-4.3.1&q=70&lossless=0&w=800&h=800&fit=clip&s=b8843b892860fbc90f526b50e35434f9) - 在七段顯示器上顯示目前的秒數,秒數的精度為 0.01 秒,需顯示小數點。 - 按下 S/P 控制碼錶運行或是暫停。 - 按下 R 鍵,若碼表暫停歸 0,若碼表運行則無動作。 - 七段顯示器與秒數的增加**限用** Timer 處理。 ### 按鍵配置 ``` ┌───┬───┬───┐ │ │ │ │ ├───┼───┼───┤ │ │S/P│ │ ├───┼───┼───┤ │ │ │ R │ └───┴───┴───┘ ``` ::: 錯誤程式 ```cpp= // // TMR_LED : change LED on/off by Timer1 interrupt // #include <stdio.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" volatile uint8_t ledState =0; int cnt_5ms=0,digit0,digit1,digit2,digit3,k; uint16_t cnt=0,KEY_Flag,run=0; void TMR1_IRQHandler(void) { if(run)cnt++; TIMER_ClearIntFlag(TIMER1); // Clear Timer1 time-out interrupt flag } void TMR2_IRQHandler(void) { cnt_5ms++; if(cnt_5ms%4==0){ CloseSevenSegment(); ShowSevenSegment(0,digit0); }else if(cnt_5ms%4==1){ CloseSevenSegment(); ShowSevenSegment(1,digit1); }else if(cnt_5ms%4==2){ CloseSevenSegment(); ShowSevenSegment(2,digit2); }else if(cnt_5ms%4==3){ CloseSevenSegment(); ShowSevenSegment(3,digit3); } TIMER_ClearIntFlag(TIMER2); } void Init_Timer1(void) { TIMER_Open(TIMER1, TMR1_OPERATING_MODE, 100); TIMER_EnableInt(TIMER1); NVIC_EnableIRQ(TMR1_IRQn); TIMER_Start(TIMER1); } void Init_Timer2(void) { TIMER_Open(TIMER2, TMR1_OPERATING_MODE, 200); TIMER_EnableInt(TIMER2); NVIC_EnableIRQ(TMR2_IRQn); TIMER_Start(TIMER2); } int main(void) { SYS_Init(); // Intialize System/Peripheral clocks & multi-function I/Os OpenSevenSegment(); OpenKeyPad(); Init_Timer1(); Init_Timer2(); while(1){ k=ScanKey(); if(k==5){ run = ~run; PC13=0; }else if(k==9){ cnt=0; } digit3=cnt/1000; digit2=(cnt - digit3*1000)/100; digit1=(cnt - digit3*1000 -digit2*100)/10; digit0=cnt%10; }; } ``` 正確程式 ```c= #include <stdio.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "Seven_Segment.h" #include "Scankey.h" #define TIMER_INTERVAL 10 // Timer interval of 10 ms, for 0.01 sec accuracy #define MAX_SECONDS 9999 // Maximum value for 99.99 seconds volatile uint16_t seconds = 0; // Store seconds value in hundredths (100 = 1.00 sec) volatile uint8_t timer_running = 0; // Timer running flag (1 = running, 0 = paused) int cnt_5ms = 0, cnt_100ms = 0; int pause = 1; int lasti = 100; // Display the seconds value (integer and hundredths) void Display_7seg(uint16_t value) { uint8_t digit; //digit=digit*100; // Display the hundreds digit (thousands place) digit = value / 1000; CloseSevenSegment(); if(digit!=0) ShowSevenSegment(3, digit); // Display thousands digit CLK_SysTickDelay(2475); value = value - digit * 1000; // Display the tens digit (hundreds place) digit = value / 100; CloseSevenSegment(); ShowSevenSegment(2, digit); PE1=0;// Display hundreds digit CLK_SysTickDelay(2475); value = value - digit * 100; // Display the ones digit (tens place) digit = value / 10; CloseSevenSegment(); ShowSevenSegment(1, digit); // Display tens digit CLK_SysTickDelay(2475); value = value - digit * 10; // Display the hundredths digit (ones place) digit = value; CloseSevenSegment(); ShowSevenSegment(0, digit); // Display ones digit CLK_SysTickDelay(2475); } // Timer1 interrupt handler (10ms interval) void TMR1_IRQHandler(void) { cnt_5ms++; if (timer_running) { // Increment the counter by 1 (representing 0.01 seconds) seconds++; // Check if seconds exceed the max value (99.99 seconds) if (seconds > MAX_SECONDS) { seconds = 0; // Reset to 0 } } // Display the updated time Display_7seg(seconds); // Clear the timer interrupt flag TIMER_ClearIntFlag(TIMER1); } // Initialize Timer1 for 5ms interrupt void Init_Timer1(void) { TIMER_Open(TIMER1, TMR1_OPERATING_MODE, 1000); // Set the timer for 10ms intervals TIMER_EnableInt(TIMER1); // Enable timer interrupt NVIC_EnableIRQ(TMR1_IRQn); // Enable Timer1 IRQ TIMER_Start(TIMER1); // Start Timer1 } // Main function int main(void) { uint16_t i; SYS_Init(); // Initialize System/Peripheral clocks & multi-function I/Os OpenKeyPad(); // Initialize Keypad OpenSevenSegment(); // Initialize Seven Segment display Init_Timer1(); // Initialize Timer1 while (1) { i = ScanKey(); // Scan keypress if (i == 5) { // S/P key pressed (toggle start/pause) //lasti=i; while(i==5){ i=ScanKey(); if(i!=5){ break; } } if (pause == 1) { timer_running = 1; // Start the timer pause = 0; // Set pause flag to 0 (running) } else { timer_running = 0; // Pause the timer pause = 1; // Set pause flag to 1 (paused) } } else if (i == 9) { //lasti=i;// R key pressed (reset) if (pause == 1) { seconds = 0; // Reset the time to 0 if the timer is paused } } } } ``` ## 12/20 RTC :::spoiler # Lab 10 - RTC ## 題目 1 - 新年快樂 ![](https://image-cdn.learnin.tw/bnextmedia/image/album/2019-12/img-1577779026-38598.jpg?w=900&output=webp) 令人期待的跨年煙火進入了倒數階段,讓我們一起到迎接 2025 年的到來吧~ - 初始時間設定為 2024/12/31 23:59:45。 - 左側第一行顯示日期,格式為 `yyyy/mm/dd`;第二行以 24 小時制顯示時間 `hh:mm:ss`;第三行顯示當日的星期。字元大小為 5x7。 - 右側畫時鐘,需畫出時針、分針與秒針。 - 在七段顯示器上顯示距離 2025 年還有多少秒。 - 當時間到 2025/01/01 00:00:00 LCD 的畫面切換成煙火的圖片,並在七段顯示器上以往左的跑馬燈顯示 "HAPPy nE- yEAr";跑馬燈結束後 LCD 回到時間畫面。 ::: ![image](https://hackmd.io/_uploads/B1_BUiGSyl.png) 主程式 ```cpp= #include <stdio.h> #include <math.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #include "LCD.h" #include "Draw2D.h" #include "Seven_Segment.h" #define PI 3.1415926 #define X0 96 #define Y0 32 #define R 20 volatile uint8_t old_sec_x, old_sec_y, old_min_x, old_min_y, old_hour_x, old_hour_y; volatile uint8_t new_sec_x, new_sec_y, new_min_x, new_min_y, new_hour_x, new_hour_y; unsigned char bmp_Clock[64*64]= { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,0x60,0x20,0x30,0xD0,0x98,0x08,0x8C,0x04,0x06,0x06,0x02,0x02,0x02,0x03,0x03,0xF1,0x01,0x13,0x93,0x51,0x31,0x03,0x03,0x02,0x02,0x02,0x06,0x06,0x04,0x0C,0x88,0x18,0x10,0x70,0x20,0x60,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0xC0,0x60,0x38,0x0C,0x06,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x03,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1D,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x06,0x0C,0x30,0x60,0xC0,0x00,0x00,0x00,0x00,0x00, 0x00,0xC0,0xF0,0x1E,0x03,0x01,0x02,0x3F,0x00,0x24,0x22,0x21,0x11,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x3B,0x29,0x27,0x00,0x02,0x01,0x07,0x1C,0xF0,0xC0,0x00, 0xF8,0x9F,0x00,0x00,0x80,0x60,0x20,0xA0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x60,0x20,0xA0,0x60,0x00,0x00,0xBF,0xF8, 0x1F,0xF9,0x00,0x00,0x01,0x05,0x05,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x05,0x05,0x03,0x00,0x00,0xFD,0x1F, 0x00,0x03,0x0F,0x78,0xC0,0x80,0x40,0x40,0xB8,0x94,0x94,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x28,0x2C,0xFC,0x00,0x40,0x80,0xE0,0x38,0x0F,0x03,0x00, 0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x1C,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x10,0x08,0xC8,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x50,0x48,0x68,0xC8,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0x60,0x30,0x1C,0x06,0x03,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x03,0x06,0x04,0x0C,0x1A,0x11,0x10,0x30,0x20,0x60,0x60,0x40,0x40,0x40,0xC0,0xC0,0x80,0x8F,0xD2,0xCA,0x8E,0x80,0xC0,0xC0,0x40,0x40,0x40,0x60,0x60,0x21,0x31,0x11,0x19,0x0A,0x0C,0x04,0x06,0x03,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; unsigned char bmp_Fireworks[64*8]= { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x30,0x70,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xE0,0x91,0x8F,0xFF,0xFF,0xFE,0xFC,0xF0,0xE0,0xC0,0x00,0x00,0x00,0x00,0x80,0xF8,0xFE,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xFC,0xFC,0xF0,0xF0,0xB0,0x10,0x10,0x18,0x18,0x08,0x08,0x08,0x0C,0x04,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0x40,0x60,0x60,0x60,0x60,0x60,0x60,0x63,0x67,0x67,0x7F,0x7F,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0xF8,0xE0,0xFF,0x7F,0xF0,0x70,0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0x7F,0x1F,0x0F,0x07,0x07,0x05,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x08,0x04,0x02,0x01,0x00,0x00,0x00,0x80,0x80,0xA0,0xE0,0xE0,0xE0,0xE0,0xF0,0xF0,0xF8,0xF0,0xF8,0xF8,0xFC,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFE,0xFE,0xFE,0xFE,0xFA,0xC3,0xE3,0xE3,0xE3,0xE3,0xE3,0xC3,0x83,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x01,0x00, 0x00,0x80,0x40,0x20,0x10,0x00,0x08,0x04,0x06,0x0F,0x0F,0x1F,0x1F,0x0F,0x1F,0x0F,0x1F,0x0F,0x2F,0x1F,0x0F,0x07,0x03,0x03,0x03,0x03,0x07,0x0F,0x0F,0x0F,0xFF,0xFF,0xFF,0xFF,0xCF,0xDF,0xFF,0xBF,0x7F,0x3F,0xFF,0xF3,0xE3,0x87,0x03,0x07,0x07,0x0F,0x1F,0x1F,0x3F,0x3F,0x0F,0x07,0x0E,0x1E,0x1C,0x3C,0x38,0x38,0x10,0x00,0x00,0x00, 0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0x10,0x0C,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBE,0x00,0x00,0x00,0x01,0x07,0x0F,0x3E,0xFC,0xF0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x3E,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0xFF,0xFF,0x81,0x07,0x07,0x03,0x03,0x03,0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x0F,0xFF,0xFE,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,}; int eflag = 0; void Display_7seg(uint16_t value) { uint8_t digit; digit = value / 10; CloseSevenSegment(); ShowSevenSegment(1,digit); CLK_SysTickDelay(500); digit = value % 10; CloseSevenSegment(); ShowSevenSegment(0,digit); CLK_SysTickDelay(500); } void r1() { CloseSevenSegment(); h(3); CLK_SysTickDelay(5000); CloseSevenSegment(); a(2); CLK_SysTickDelay(5000); CloseSevenSegment(); p(1); CLK_SysTickDelay(5000); CloseSevenSegment(); p(0); CLK_SysTickDelay(5000); } void r2() { CloseSevenSegment(); a(3); CLK_SysTickDelay(5000); CloseSevenSegment(); p(2); CLK_SysTickDelay(5000); CloseSevenSegment(); p(1); CLK_SysTickDelay(5000); CloseSevenSegment(); y(0); CLK_SysTickDelay(5000); } void r3() { CloseSevenSegment(); p(3); CLK_SysTickDelay(5000); CloseSevenSegment(); p(2); CLK_SysTickDelay(5000); CloseSevenSegment(); y(1); CLK_SysTickDelay(5000); CloseSevenSegment(); space(0); CLK_SysTickDelay(5000); } void r4() { CloseSevenSegment(); p(3); CLK_SysTickDelay(5000); CloseSevenSegment(); y(2); CLK_SysTickDelay(5000); CloseSevenSegment(); space(1); CLK_SysTickDelay(5000); CloseSevenSegment(); n(0); CLK_SysTickDelay(5000); } void r5() { CloseSevenSegment(); y(3); CLK_SysTickDelay(5000); CloseSevenSegment(); space(2); CLK_SysTickDelay(5000); CloseSevenSegment(); n(1); CLK_SysTickDelay(5000); CloseSevenSegment(); e(0); CLK_SysTickDelay(5000); } void r6() { CloseSevenSegment(); space(3); CLK_SysTickDelay(5000); CloseSevenSegment(); n(2); CLK_SysTickDelay(5000); CloseSevenSegment(); e(1); CLK_SysTickDelay(5000); CloseSevenSegment(); m(0); CLK_SysTickDelay(5000); } void r7() { CloseSevenSegment(); n(3); CLK_SysTickDelay(5000); CloseSevenSegment(); e(2); CLK_SysTickDelay(5000); CloseSevenSegment(); m(1); CLK_SysTickDelay(5000); CloseSevenSegment(); space(0); CLK_SysTickDelay(5000); } void r8() { CloseSevenSegment(); e(3); CLK_SysTickDelay(5000); CloseSevenSegment(); m(2); CLK_SysTickDelay(5000); CloseSevenSegment(); space(1); CLK_SysTickDelay(5000); CloseSevenSegment(); y(0); CLK_SysTickDelay(5000); } void r9() { CloseSevenSegment(); m(3); CLK_SysTickDelay(5000); CloseSevenSegment(); space(2); CLK_SysTickDelay(5000); CloseSevenSegment(); y(1); CLK_SysTickDelay(5000); CloseSevenSegment(); e(0); CLK_SysTickDelay(5000); } void r10() { CloseSevenSegment(); space(3); CLK_SysTickDelay(5000); CloseSevenSegment(); y(2); CLK_SysTickDelay(5000); CloseSevenSegment(); e(1); CLK_SysTickDelay(5000); CloseSevenSegment(); a(0); CLK_SysTickDelay(5000); } void r11() { CloseSevenSegment(); y(3); CLK_SysTickDelay(5000); CloseSevenSegment(); e(2); CLK_SysTickDelay(5000); CloseSevenSegment(); a(1); CLK_SysTickDelay(5000); CloseSevenSegment(); r(0); CLK_SysTickDelay(5000); } void Draw_ClockPointers(S_RTC_TIME_DATA_T *sCurTime) { uint16_t sec_theta = (sCurTime->u32Second + 15) * 6; uint16_t min_theta = (sCurTime->u32Minute + 15) * 6 + sCurTime->u32Second / 10; uint16_t hour_theta = ((sCurTime->u32Hour + 3) % 12) * 30 + sCurTime->u32Minute / 2; // ????? new_sec_x = X0 - (R - 2) * cos(sec_theta * PI / 180); new_sec_y = Y0 - (R - 2) * sin(sec_theta * PI / 180); new_min_x = X0 - (R - 6) * cos(min_theta * PI / 180); new_min_y = Y0 - (R - 6) * sin(min_theta * PI / 180); new_hour_x = X0 - (R - 10) * cos(hour_theta * PI / 180); new_hour_y = Y0 - (R - 10) * sin(hour_theta * PI / 180); // ?????? if (eflag == 0){ draw_Line(old_sec_x, old_sec_y, X0, Y0, BG_COLOR, BG_COLOR); draw_Line(old_min_x, old_min_y, X0, Y0, BG_COLOR, BG_COLOR); draw_Line(old_hour_x, old_hour_y, X0, Y0, BG_COLOR, BG_COLOR); // ????? draw_Line(new_sec_x, new_sec_y, X0, Y0, FG_COLOR, BG_COLOR); draw_Line(new_min_x, new_min_y, X0, Y0, FG_COLOR, BG_COLOR); draw_Line(new_hour_x, new_hour_y, X0, Y0, FG_COLOR, BG_COLOR); } // ?????? old_sec_x = new_sec_x; old_sec_y = new_sec_y; old_min_x = new_min_x; old_min_y = new_min_y; old_hour_x = new_hour_x; old_hour_y = new_hour_y; } void RTC_IRQHandler(void) { char strDate[16], strTime[16], strWeek[16]; S_RTC_TIME_DATA_T sCurTime; RTC_GetDateAndTime(&sCurTime); // ?????? // ??????????? LCD if(eflag == 0){ sprintf(strDate, "%04d/%02d/%02d", sCurTime.u32Year, sCurTime.u32Month, sCurTime.u32Day); sprintf(strTime, "%02d:%02d:%02d", sCurTime.u32Hour, sCurTime.u32Minute, sCurTime.u32Second); sprintf(strWeek, "WEEK:%d", sCurTime.u32DayOfWeek); printS_5x7(0, 0, strDate); printS_5x7(0, 8, strTime); printS_5x7(0, 16, strWeek); // ????? Draw_ClockPointers(&sCurTime); } RTC_CLEAR_TICK_INT_FLAG(); } void Init_RTC(void) { S_RTC_TIME_DATA_T sInitTime; // ?????? sInitTime.u32Year = 2024; sInitTime.u32Month = 12; sInitTime.u32Day = 31; sInitTime.u32Hour = 23; sInitTime.u32Minute = 59; sInitTime.u32Second = 45; sInitTime.u32DayOfWeek = RTC_TUESDAY; sInitTime.u32TimeScale = RTC_CLOCK_24; RTC_Open(&sInitTime); RTC_SetTickPeriod(RTC_TICK_1_SEC); RTC_EnableInt(RTC_RIER_TIER_Msk); NVIC_EnableIRQ(RTC_IRQn); } int32_t main(void) { int flag = 1; SYS_Init(); init_LCD(); clear_LCD(); draw_Bmp64x64(64, 0, FG_COLOR, BG_COLOR, bmp_Clock); // ?? 45 ?????? old_sec_x = X0 - (R - 2) * cos(45 * 6 * PI / 180); old_sec_y = Y0 - (R - 2) * sin(45 * 6 * PI / 180); old_min_x = X0 - (R - 6) * cos(59 * 6 * PI / 180); // ????? 59 old_min_y = Y0 - (R - 6) * sin(59 * 6 * PI / 180); old_hour_x = X0 - (R - 10) * cos(11 * 30 * PI / 180); // ????? 23 old_hour_y = Y0 - (R - 10) * sin(11 * 30 * PI / 180); Init_RTC(); // ??? RTC,? 23:59:45 ?? while (1) { int i = 0; S_RTC_TIME_DATA_T sCurTime; RTC_GetDateAndTime(&sCurTime); if (flag == 1) { Display_7seg(60 - sCurTime.u32Second); } if (60 - sCurTime.u32Second == 1) { flag = 0; } // Check if the time is 2025/01/01 00:00:00 if (sCurTime.u32Year == 2025 && sCurTime.u32Month == 1 && sCurTime.u32Day == 1 && sCurTime.u32Hour == 0 && sCurTime.u32Minute == 0 && sCurTime.u32Second == 0) { eflag = 1; clear_LCD(); draw_Bmp64x64(32, 0, FG_COLOR, BG_COLOR, bmp_Fireworks); // Fireworks display for (i = 0;i < 50;i++){ r1(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r2(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r3(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r4(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r5(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r6(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r7(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r8(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r9(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r10(); CLK_SysTickDelay(1000); } for (i = 0;i < 50;i++){ r11(); CLK_SysTickDelay(1000); } // Marquee message //r2(); clear_LCD(); // Clear and return to clock draw_Bmp64x64(64, 0, FG_COLOR, BG_COLOR, bmp_Clock); CloseSevenSegment(); eflag = 0; } } } ``` Seven_Segment.c ```cpp= #include <stdio.h> #include "NUC100Series.h" #include "GPIO.h" #include "SYS.h" #include "Seven_Segment.h" #define SEG_N0 0x82 #define SEG_N1 0xEE #define SEG_N2 0x07 #define SEG_N3 0x46 #define SEG_N4 0x6A #define SEG_N5 0x52 #define SEG_N6 0x12 #define SEG_N7 0xE6 #define SEG_N8 0x02 #define SEG_N9 0x62 #define SEG_N10 0x22 #define SEG_N11 0x1A #define SEG_N12 0x93 #define SEG_N13 0x0E #define SEG_N14 0x13 #define SEG_N15 0x33 uint8_t SEG_BUF[16]={SEG_N0, SEG_N1, SEG_N2, SEG_N3, SEG_N4, SEG_N5, SEG_N6, SEG_N7, SEG_N8, SEG_N9, SEG_N10, SEG_N11, SEG_N12, SEG_N13, SEG_N14, SEG_N15}; void OpenSevenSegment(void) { GPIO_SetMode(PC, BIT4, GPIO_PMD_OUTPUT); GPIO_SetMode(PC, BIT5, GPIO_PMD_OUTPUT); GPIO_SetMode(PC, BIT6, GPIO_PMD_OUTPUT); GPIO_SetMode(PC, BIT7, GPIO_PMD_OUTPUT); PC4=0; PC5=0; PC6=0; PC7=0; GPIO_SetMode(PE, BIT0, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT1, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT2, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT3, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT4, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT5, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT6, GPIO_PMD_QUASI); GPIO_SetMode(PE, BIT7, GPIO_PMD_QUASI); PE0=0; PE1=0; PE2=0; PE3=0; PE4=0; PE5=0; PE6=0; PE7=0; } void ShowSevenSegment(uint8_t no, uint8_t number) { uint8_t temp,i; temp=SEG_BUF[number]; for(i=0;i<8;i++) { if((temp&0x01)==0x01) switch(i) { case 0: PE0=1; break; case 1: PE1=1; break; case 2: PE2=1; break; case 3: PE3=1; break; case 4: PE4=1; break; case 5: PE5=1; break; case 6: PE6=1; break; case 7: PE7=1; break; } else switch(i) { case 0: PE0=0; break; case 1: PE1=0; break; case 2: PE2=0; break; case 3: PE3=0; break; case 4: PE4=0; break; case 5: PE5=0; break; case 6: PE6=0; break; case 7: PE7=0; break; } temp=temp>>1; } switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void CloseSevenSegment(void) { PC4=0; PC5=0; PC6=0; PC7=0; } void h(uint8_t no) { PE0=0; PE1=1; PE2=0; PE3=1; PE4=0; PE5=1; PE6=0; PE7=0; switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void a(uint8_t no) { PE0=0; PE1=1; PE2=0; PE3=0; PE4=0; PE5=1; PE6=0; PE7=0; switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void p(uint8_t no) { PE0=1; PE1=1; PE2=0; PE3=0; PE4=0; PE5=1; PE6=0; PE7=0; switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void y(uint8_t no) { PE0=0; PE1=1; PE2=0; PE3=1; PE4=0; PE5=0; PE6=1; PE7=0; switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void space(uint8_t no) { PE0=1; PE1=1; PE2=1; PE3=1; PE4=1; PE5=1; PE6=1; PE7=1; switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void n(uint8_t no) { PE0=0; PE1=1; PE2=1; PE3=1; PE4=1; PE5=1; PE6=0; PE7=0; switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void e(uint8_t no) { PE0=1; PE1=1; PE2=0; PE3=0; PE4=1; PE5=0; PE6=0; PE7=0; switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void r(uint8_t no) { PE0=1; PE1=1; PE2=1; PE3=1; PE4=1; PE5=1; PE6=0; PE7=0; switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } void m(uint8_t no) { PE0=1; PE1=1; PE2=1; PE3=1; PE4=1; PE5=1; PE6=1; PE7=0; switch(no) { case 0: PC4=1; break; case 1: PC5=1; break; case 2: PC6=1; break; case 3: PC7=1; break; } } ``` ## 12/27 PWM and UART :::spoiler # Lab 11 - PWM and UART ## 題目 1 - 變色燈 ![RGB](https://hackmd.io/_uploads/r1MURZ-Skx.png) - 初始狀態下, RGB 顏色為紅色。 - 利用 INT1 控制呼吸燈是否要變換顏色。 - 在運作的過程,變色燈從目前的顏色漸變成下個顏色,變化順序為「紅」->「綠」->「藍」,週期為 6 秒。變化過程見上方圖表。 ## 【加分】題目 2 - 猜數字 設計一組「出題機」與「猜題機」(可兩人一組)。 ### 共通部分 - 數字範圍 -128~127,包含 -128 與 127。 - 限用一個 UART 傳送與接收資料。 ### 出題機 - 在七段顯示器上顯示目標的數字。 - 按下 INT1 時,隨機產生一個新的數字。 - UART 接收到資料若為「數值」,與目前的數字做比較: - 若接收到的數字大於目標數字,回傳 1。 - 若接收到的數字等於目標數字,回傳 0。 - 若接收到的數字小於目標數字,回傳 -1。 - 在 LCD 顯示最後猜測的 8 個數字,字元大小為 5x7。 ### 猜題機 - 在七段顯示器上顯示設定的數字。 - 在 LCD 第一行顯示猜測的次數,第二行顯示目標數字的範圍,輸出格式如下 ``` ┌────────────────┐ │ Count: 3 │ │ │ │ Min: -20 │ │ Max: 48 │ └────────────────┘ ``` - 利用按鍵更改其數值,需檢查範圍。 ``` ┌───┬───┬───┐ │-10│ │+10│ ├───┼───┼───┤ │ -5│ │ +5│ ├───┼───┼───┤ │ -1│ │ +1│ └───┴───┴───┘ ``` - 按下 INT1 傳送猜測的數字,並依照回傳值更新。 - 若猜中數字,依照下面的格式在 LCD 印出猜測次數。 - 若沒猜中數字,更新猜測範圍。 ``` ┌────────────────┐ │ You Win!! │ │ │ │ You guested │ │ 12 time(s). │ └────────────────┘ ``` ::: ![IMG_6096](https://hackmd.io/_uploads/H1FHeAGLJl.jpg) ```c= #include <stdio.h> #include "NUC100Series.h" #include "MCU_init.h" #include "SYS_init.h" #define MIN_BRIGHTNESS 1 // ??????? int flag=0; void EINT1_IRQHandler(void) { GPIO_CLR_INT_FLAG(PB, BIT15); // Clear GPIO interrupt flag if(flag==0) flag=1; else if(flag==1) flag=0; } void Init_EXTINT(void) { // Configure EINT1 pin and enable interrupt by rising and falling edge trigger GPIO_SetMode(PB, BIT15, GPIO_MODE_INPUT); GPIO_EnableEINT1(PB, 15, GPIO_INT_RISING); // RISING, FALLING, BOTH_EDGE, HIGH, LOW NVIC_EnableIRQ(EINT1_IRQn); // Enable interrupt de-bounce function and select de-bounce sampling cycle time GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_64); GPIO_ENABLE_DEBOUNCE(PB, BIT15); } void Init_GPIO(void) { GPIO_SetMode(PA, BIT12, GPIO_MODE_OUTPUT); GPIO_SetMode(PA, BIT13, GPIO_MODE_OUTPUT); GPIO_SetMode(PA, BIT14, GPIO_MODE_OUTPUT); GPIO_SetMode(PC, BIT12, GPIO_MODE_OUTPUT); GPIO_SetMode(PC, BIT13, GPIO_MODE_OUTPUT); GPIO_SetMode(PC, BIT14, GPIO_MODE_OUTPUT); GPIO_SetMode(PC, BIT15, GPIO_MODE_OUTPUT); PA12=1; //B PA13=1; //G PA14=1; //R } void Init_PWM(void) { PWM_ConfigOutputChannel(PWM0, 0, 125000, 100); //B PWM_ConfigOutputChannel(PWM0, 1, 125000, 100); //G PWM_ConfigOutputChannel(PWM0, 2, 125000, 0); //R PWM_EnableOutput(PWM0, (PWM_CH_0_MASK | PWM_CH_1_MASK |PWM_CH_2_MASK)); PWM_Start(PWM0, (PWM_CH_0_MASK | PWM_CH_1_MASK |PWM_CH_2_MASK)); } void Change_Color() { static uint8_t current_phase = 0; // ???? (0: ?->?, 1: ?->?, 2: ?->?) static uint32_t step_count = 0; // ????? const uint8_t max_brightness = 100; // ???? const uint32_t total_steps = 100; // ?????? if (flag) // ???????? { if (step_count <= total_steps) { switch (current_phase) { case 0: // ? -> ? PWM_SET_CMR(PWM0, 2, max_brightness - step_count); // ?????? PWM_SET_CMR(PWM0, 1, step_count); // ?????? PWM_SET_CMR(PWM0, 0, 0); // ?????? break; case 1: // ? -> ? PWM_SET_CMR(PWM0, 1, max_brightness - step_count); // ?????? PWM_SET_CMR(PWM0, 0, step_count); // ?????? PWM_SET_CMR(PWM0, 2, 0); // ?????? break; case 2: // ? -> ? PWM_SET_CMR(PWM0, 0, max_brightness - step_count); // ?????? PWM_SET_CMR(PWM0, 2, step_count); // ?????? PWM_SET_CMR(PWM0, 1, 0); // ?????? break; } step_count++; // ????? CLK_SysTickDelay(20 * 1000); // ?? 20ms } else { step_count = 0; // ????? current_phase = (current_phase + 1) % 3; // ??????? } } } int32_t main(void) { SYS_Init(); Init_PWM(); Init_GPIO(); Init_EXTINT(); while(1){ Change_Color(); } } ``` ## 畫螺線 ![未命名](https://hackmd.io/_uploads/H1_YibHU1e.bmp) ```c= void Draw_Spiral(uint16_t control_value) { //版本1 旋轉螺線 函式變數要自己改 LCD_Clear(0); // 清除畫面 float centerX = 64; // LCD 的中心 X float centerY = 32; // LCD 的中心 Y float fixedRadius = 30; // 固定半徑 float angleStep = 0.1; // 螺線的角度步進 // 根據 VR1 計算旋轉偏移角度 float rotationOffset = (control_value / 4096.0) * 2 * 3.14159; // 0 ~ 2π 的旋轉 for (float theta = 0; theta < 6.28 * 5; theta += angleStep) { // 畫出多圈 // 套用旋轉偏移量 float x = centerX + fixedRadius * cos(theta + rotationOffset); float y = centerY + fixedRadius * sin(theta + rotationOffset); LCD_DrawPixel((int)x, (int)y); // 繪製點 } } ``` ```c= void Draw_Spiral(uint16_t control_value) { //版本2 變半徑螺線 LCD_Clear(0); // 清除畫面 float centerX = 64; // LCD 的中心 X float centerY = 32; // LCD 的中心 Y float maxRadius = control_value / 10.0; // 根據 VR1 控制最大半徑 float angleStep = 0.1; // 螺線的角度步進 for (float theta = 0; theta < 6.28 * 5; theta += angleStep) { // 畫出多圈 float radius = maxRadius * theta / (6.28 * 5); int x = centerX + radius * cos(theta); int y = centerY + radius * sin(theta); LCD_DrawPixel(x, y); // 繪製點 } } ```