# HW5-數位式時鐘設計
### 班級:資工三乙
### 座號:046
### 姓名:陳德恩
### 學號:1110832098
### 一.實驗標題:數位式時鐘設計
### 二.指導老師:林宏益 老師
### 三.報告內容:
#### 1.實驗目的:
認識數位式時鐘設計實作方式,建立硬體開發之實務能力,建立硬體開發之技能,亦透過此次作業來練習電路設計與微處理機軟體設計能力。
#### 2.實驗原理:
透過Arduino C程式語言以及thinkercad開發模擬軟體來實際開發數位式時鐘設計。
#### 3實驗材料:
此次開發均在thinkercad 模擬軟體開發與測試,下列元件均為虛擬元件。
|Arduino 開發版 | 單一通道指撥開關 | 電阻 |PMOS電晶體|七段顯示器|LED|四腳式開關|
| -------- | -------- | -------- |-------- |-------- |-------- |-------- |
| 開發版x1 |1個 | 10k歐姆的9個<br>10歐姆的7個|4個|4個|1個|4個|
#### 4.數位式時鐘簡介:
一個可以計時的數位時鐘,上面可顯示目前累積的計時小時數與分鐘數,當撥下指撥開關的時候就會開始計時,時間計時範圍:12:00 ~ 22:00,接著返回到12:00繼續計時。
#### 5.實驗步驟:
(1).設計一個電路,參考課本4-67頁的電路圖進行電路設計。
(2).依據亮度以及元件之特性修改電阻大小。
(3).修改程式碼。
(4).美化線路排列,以減少除錯時間。
#### 6.成品說明:
實驗要求:理論開始計時時會顯示12:00,接者開始變成12:01、12:02等等數字組合,一直到最後是22:00。
完成的部分:計時顯示從00:00~59:59。
#### 7.原本的程式碼(計時從00:00~59:59):
``` clike=
/*
範例 ch4_9
蜂鳴器BZ連接在13腳
七節顯示器之G~A連接在12~6腳
七節顯示器之com1~com4連接在5~2腳
指撥開關(P5-1)連接在A0腳
七節顯示器之閃秒點DD連接在A1腳
4x4鍵盤之C3~C0連接在A5~A2腳(PB[3]~PB[0])
4x4鍵盤之R0連接GND
*/
const int SEG[7] = {6,7,8,9,10,11,12}; // 宣告顯示信號接腳
const int scan[4] = {2,3,4,5}; // 宣告掃描信號接腳(scan)
const int SEG_code[10] = { // 宣告七節顯示碼陣列
0xc0,0xf9,0xa4,0xb0,0x99, // 0~4
0x92,0x82,0xf8,0x80,0x90 } ; // 5~9
const int PB[4]={A2,A3,A4,A5}; // 按鍵
const int DD=A1; // 閃秒LED
const int DIPSW=A0; // 指撥開關
const int BZ=13; // 蜂鳴器
int disp[4]={0,0,0,0}; // 宣告顯示緩衝區陣列
int DISP; // 宣告顯示變數
unsigned long x0,x1,x2; // 宣告時間變數
// 宣告工作旗標:phase0=false計時模式(ON), phase0=true調整模式(OFF)
boolean phase0;
int seconds=0,sec=0; // 宣告秒變數
void beep(int pin, int counts); // 宣告嗶聲函數
void scanner(int x); // 宣告掃描函數
// 初始設定
void setup() {
for(int i=0;i<7;i++)
pinMode(SEG[i], OUTPUT); // 設定顯示信號接腳為輸出接腳
for(int i=0;i<4;i++) {
pinMode(scan[i], OUTPUT); // 設定顯示掃描接腳為輸出接腳
pinMode(PB[i], INPUT); // 設定PB(行接腳)為輸入接腳
}
pinMode(DD, OUTPUT); // 設定閃秒LED接腳為輸出接腳
pinMode(BZ, OUTPUT); // 設定蜂鳴器接腳為輸出接腳
pinMode(DIPSW, INPUT); // 設定指撥開關接腳為輸入接腳
}
// 主程式
void loop() {
if (digitalRead(DIPSW)) phase0=true;//讀取指撥開關true:調整模式OFF
else phase0=false; //false:計時模式ON
//調整模式
while(phase0) {
seconds=0; //秒數歸零
//時遞增調整(按鍵為低態動作)
if (!digitalRead(PB[0])) { //若PB[0]被按下(時遞增)
if (disp[3]==2 && disp[2]==3) { //若已23時
disp[3]=0; //時之十位數歸零
disp[2]=0; //時之個位數歸零
}
else if (disp[2]==9) { //若時之個位數等於9
disp[2]=0; //時之個位數歸零
disp[3]++; //時之十位數遞增(進位)
}
else disp[2]++; //否則時之個位數遞增
}
//時遞減調整(按鍵為低態動作)
if (!digitalRead(PB[1])) { //若PB[1]被按下(時遞減)
if (disp[3]==0 && disp[2]==0) { //若已00時
disp[3]=2; //時之十位數調整為2
disp[2]=3; //時之個位數調整為3
}
else if (disp[2]==0) { //若時之個位數等於0
disp[2]=9; //時之個位調整為9
disp[3]--; //時之十位數遞減(借位)
}
else disp[2]--; //否則時之個位數遞減
}
//分遞增調整(按鍵為低態動作)
if (!digitalRead(PB[2])) { //若PB[2]被按下(分遞增)
if (disp[1]==5 && disp[0]==9) { //若已59分
disp[1]=0; //分之十位數歸零
disp[0]=0; //分之個位數歸零
}
else if (disp[0]==9) { //若分之個位數等於9
disp[0]=0; //分之個位數歸零
disp[1]++; //分之十位數遞增(進位)
}
else disp[0]++; //否則分之個位數遞增
}
//分遞減調整(按鍵為低態動作)
if (!digitalRead(PB[3])) { //若PB[3]被按下(分遞減)
if (disp[1]==0 && disp[0]==0) { //若已00分
disp[1]=5; //分之十位數調整為5
disp[0]=9; //分之個位數調整為9
}
else if (disp[0]==0) { //若分之個位數等於0
disp[0]=9; //分之個位調整為9
disp[1]--; //分之十位數遞減(借位)
}
else disp[0]--; //否則分之個位數遞減
}
scanner(25); //連續掃描25週(100ms,每秒約10次)
if (digitalRead(DIPSW)) phase0=true;//讀取指撥開關true:調整模式OFF
else phase0=false; //false:計時模式ON
}
//計時模式
while(!phase0) {
scanner(2); //連續掃描2週(8ms)
x1=millis(); //查詢時間
x2=x1-x0; //計算時間差
if (x2>=500) { //0.5秒時間差
x0=x1; //記錄時間
sec=!sec; //切換閃秒
digitalWrite(DD,sec);//處理時間
//處理時間
if (sec) { //處理秒數
if (++seconds==60) { //秒數加1
seconds=0; //若秒數滿60則歸零
if (disp[0]==9) { //若分之個位數滿9
disp[0]=0; //則分之個位數歸零
if (disp[1]==5) { //若分之十位數滿5
disp[1]=0; //則分之十位數歸零
if (disp[2]==3 && disp[3]==2) {//若為23時
disp[2]=0; //則時之個位數歸零
disp[3]=0; //則時之十位數歸零
}
else if (disp[2]==9) {//若時之個位數滿9
disp[2]=0; //則時之個位數歸零
disp[3]++; //時之十位數加1
}
else disp[2]++; //否則時之個位數加1
}
else disp[1]++; //否則分之十位數加1
}
else disp[0]++; //否則分之個位數加1
}
}
}
if (digitalRead(DIPSW)) phase0=true;//讀取指撥開關true:調整模式OFF
else phase0=false; //false:計時模式ON
}
}
//=== 嗶聲函數 ================================
void beep(int pin, int counts) {
for(int i=0;i<counts;i++) { //執行counts次
tone(pin,1000,100); //發聲(1kHz,0.1秒)
delay(100); //靜音(0.1秒)
}
}
//=== 七節顯示器掃描函數========================
void scanner(int x) { // G~A(不含小數點)每週期4ms
for(int i=0;i<x;i++) { //重複掃描x次
for(int j=0;j<4;j++) { //掃描四位數
//關閉掃描信號(防殘影)
for(int k=0;k<4;k++) digitalWrite(scan[k],1);//掃描信號為1111
//輸出七節顯示器顯示信號
DISP=SEG_code[disp[j]]; // 讀取顯示信號
for(int k=0;k<8;k++) { // 並列資料串列輸出
if(bitRead(DISP,k)) digitalWrite(SEG[k],1);
else digitalWrite(SEG[k],0);
}
//輸出掃描信號
digitalWrite(scan[j],0);
delay(1); //延遲1ms
}
}
}
```
#### 8.修改的程式碼(計時從12:00~22:00):
``` clike=
const int SEG[7] = {6, 7, 8, 9, 10, 11, 12},
scan[4] = {2, 3, 4, 5},
SEG_code[10] = {
0xc0,0xf9,0xa4,0xb0,0x99,
0x92,0x82,0xf8,0x80,0x90
},
PB[4] = {A2, A3, A4, A5};
const int DD = A1, DIPSW = A0;
int disp[4] = {0, 0, 2, 1};
int DISP, seconds = 0, sec = 0;
unsigned long x0, x1, x2;
boolean phase0;
char buf[50] = {};
void scanner(int x);
void showSerial(boolean state);
void setup() {
for(int i = 0; i < 7; i++)
pinMode(SEG[i], OUTPUT);
for(int i = 0; i < 4; i++) {
pinMode(scan[i], OUTPUT);
pinMode(PB[i], INPUT);
}
pinMode(DD, OUTPUT);
pinMode(DIPSW, INPUT);
Serial.begin(9600);
}
void loop() {
phase0 = digitalRead(DIPSW);
while(phase0) {
seconds=0;
if (!digitalRead(PB[0])) {
if (disp[3] == 2 && disp[2] == 1) {
disp[3] = 1;
disp[2] = 2;
}
else if (disp[2] == 9) {
disp[2] = 0;
disp[3]++;
}
else disp[2]++;
showSerial(phase0);
}
if (!digitalRead(PB[1])) {
if (disp[3] == 1 && disp[2] == 2) {
disp[3] = 2;
disp[2] = 1;
}
else if (!disp[2]) {
disp[2] = 9;
disp[3]--;
}
else disp[2]--;
showSerial(phase0);
}
if (!digitalRead(PB[2])) {
if (disp[1] == 5 && disp[0] == 9) {
disp[1] = 0;
disp[0] = 0;
}
else if (disp[0] == 9) {
disp[0] = 0;
disp[1]++;
}
else disp[0]++;
showSerial(phase0);
}
if (!digitalRead(PB[3])) {
if (!disp[1] && !disp[0]) {
disp[1] = 5;
disp[0] = 9;
}
else if (!disp[0]) {
disp[0] = 9;
disp[1]--;
}
else disp[0]--;
showSerial(phase0);
}
scanner(25);
phase0 = digitalRead(DIPSW);
}
while(!phase0) {
scanner(2);
x1 = millis();
x2 = x1-x0;
if (x2 >= 500) {
x0 = x1;
sec = !sec;
digitalWrite(DD,sec);
if (sec) {
if (++seconds == 60) {
seconds = 0;
if (disp[0] == 9) {
disp[0] = 0;
if (disp[1] == 5) {
disp[1] = 0;
if (disp[2] == 1 && disp[3] == 2) {
disp[2] = 2;
disp[3] = 1;
}
else if (disp[2] == 9) {
disp[2] = 0;
disp[3]++;
}
else disp[2]++;
}
else disp[1]++;
}
else disp[0]++;
showSerial(phase0);
}
}
}
phase0 = digitalRead(DIPSW);
}
}
void scanner(int x) {
for(int i = 0; i < x; i++) {
for(int j = 0; j < 4; j++) {
for(int k = 0; k < 4; k++)
digitalWrite(scan[k], 1);
DISP = SEG_code[disp[j]];
for(int k = 0; k < 8; k++)
digitalWrite(SEG[k], bitRead(DISP,k));
digitalWrite(scan[j], 0);
delay(1);
}
}
}
void showSerial(boolean state){
if(state)
sprintf(buf, "input time : %d%d:%d%d\n", disp[3], disp[2], disp[1], disp[0]);
else
sprintf(buf, "now : %d%d:%d%d\n", disp[3], disp[2], disp[1], disp[0]);
Serial.print(buf);
}
```
#### 9.程式問題討論:
問題:關於時間顯示的while 迴圈,作者把小時顯示寫在內層,分鐘顯示寫在外層,如果把分鐘顯示寫在內層迴圈,小時顯示寫在外層會有什麼問題?
答案:我認為若把分鐘顯示寫在內層,小時顯示寫在外層,會發生小時跟分鐘區塊顯示相反(顯示小時的區塊變顯示分鐘,顯示分鐘的區塊變顯示小時)。
#### 10.實驗結果:

#### 圖一.計時器計時2分鐘以上,3分鐘未滿,數字顯示00:02。

#### 圖二.計時器計時3分鐘以上,4分鐘未滿,數字顯示00:03。

#### 圖三.計時器計時1分鐘以上,2分鐘未滿,數字顯示12:01(修改過後的版本)。
#### 11.實驗討論:
此次實驗有三個核心要點
第一.七段顯示器正常顯示與計時,顯示時間從00:00~59:59
第二.顯示時間從12:00~22:00,接者返回12:00計時
第三.軟體設計討論:關於時間顯示的while 迴圈,作者把小時顯示寫在內層,分鐘顯示寫在外層,如果把分鐘顯示寫在內層迴圈,小時顯示寫在外層會有什麼問題?
完成項目:
這三個核心重點我完成了第一、第二與第三,我的七段顯示器可以正常計時與顯示(可以顯示核心重點1與2的版本),亮度也是足夠明顯的,第三個重點我認為會顯示相反(小時區塊變成顯示分鐘,分鐘區塊變成顯示小時)。
#### 12.實驗心得:
我覺得這次的實驗作業有挑戰度,從一開始的電路設計再到後面的軟體設計都富有挑戰,此外,透過思考的方式也能夠更加了解背後的程式原理與微處理機運作原理,經過這次的實驗,我更加了解數位時鐘的原理,也更加認識電路設計的能力,透過多次測試來設計適合電阻值,期許之後可以在硬體開發這方面的技能更上一層樓。
#### 13.參考文獻:
艾迪諾 西元2015年 Arduino全能微處理機實習強效解析 p.4-67~p.4-77