# 雞巴微處理機
太多東西 其他放在
[雞巴微處理機2](https://hackmd.io/W7gQ3FF9ThynDiRpXadflA)

## 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。
#### 第二階段
完成所有功能。 -->
---
## 紅綠燈

- 在 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 位址檢查

輸入 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 - 視覺化最小堆積樹

利用陣列 `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列
每個節點印出的位置如下圖

```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 - 飲料販賣機

販賣機裡面販賣數種飲料,以下是這台販賣機有提供的飲料:
| 中文名 | 英文名 | 價格 |
|---------|-------------|---------|
| 綠茶 | 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 - 影像變形

- 下載兩個角色的 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

#### bandee_wiiDX_64x64.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 - 圓內接正多邊形
<!--  -->
<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 - 貪吃蛇(一)

- 在 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 - 貪吃蛇 (二)

在 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 - 迷宮設計

請設計 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。

這個 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 - 亮度可控七段顯示器

在七段顯示器上顯示其 COM 端的平均電壓。
- 顯示格式到小數第三位,要顯示小數點。
- 利用 VR 控制亮度,最左邊為「亮度 0%」而最右邊為「亮度 100%」。
- 當 INT1 按下的時候,以「亮度 100%」顯示 COM 端的平均電壓,否則以該亮度顯示其數值。
P.S. 翻手冊找 COM 端是幾 V。
### Hint
請利用「開」的時間比例達成目的,詳細參閱[維基百科 Duty Cycle 的說明](https://en.wikipedia.org/wiki/Duty_cycle)。
## 題目 2 - 蹺蹺板

- 蹺蹺板的木板長度為 80 pixel
- VR 置中時,翹翹板的木板呈水平位置;往左/右轉到底時木板偏移角度往左/右轉 20 度,旋轉軸心在木板正中間。
- 在七段顯示器賞顯示目前的偏移角度,顯示整數部分即可,右轉為正向,若有需要顯示負號。
:::

### 第一題
Library要記得加


這裡要改
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);
}
}
```

### 第二題
主程式
```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????
}
}
```

這裡要改
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 - 碼錶

- 在七段顯示器上顯示目前的秒數,秒數的精度為 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 - 新年快樂

令人期待的跨年煙火進入了倒數階段,讓我們一起到迎接 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 回到時間畫面。
:::

主程式
```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 顏色為紅色。
- 利用 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). │
└────────────────┘
```
:::

```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();
}
}
```
## 畫螺線

```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); // 繪製點
}
}
```