Compare commits

...

4 Commits

Author SHA1 Message Date
wancat
29c0a6b037 Finish week5 report 2022-03-28 23:20:34 +08:00
wancat
6b661f1d48 Week 5 2022-03-23 12:05:25 +08:00
wancat
d56ea0dab0 Week 4 2022-03-16 10:54:01 +08:00
wancat
7cf9de2f1d Week3 2022-03-09 11:21:32 +08:00
14 changed files with 716 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
*.aux
*.fdb_latexmk
*.fls
*.log
*.xdv

126
tpl.tex Normal file
View File

@@ -0,0 +1,126 @@
\documentclass[12pt,a4paper]{article}
\usepackage[top=0.75in, bottom=0.75in, left=0.75in, right=0.75in]{geometry}
\usepackage[utf8]{inputenc}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{xurl}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{booktabs}
\lstset{ % General setup for the package
language=C,
basicstyle=\footnotesize\ttfamily,
numbers=left,
numberstyle=\small,
frame=tb,
tabsize=4,
columns=fixed,
showstringspaces=false,
showtabs=false,
keepspaces,
commentstyle=\color{gray},
keywordstyle=\color{blue}
}
\usepackage{xeCJK}
\setCJKmainfont{TW-Kai}
\setCJKmonofont{Noto Sans Mono CJK TC}
\author{1082B0006 林宏信}
\title{}
\begin{document}
\begin{titlepage}
\begin{center}
\vspace*{1cm}
\Huge
\textbf{國立臺北科技大學}
\vspace{0.5cm}
\textbf{學生實習心得報告}
\vspace{1.5cm}
\textbf{}
\vspace*{\fill}
\begin{flushleft}
\large
學期110 學年度第 2 學期\\
科別:智慧自動化控制科 \\
班級:三年級 \\
學號1082B0004、1082B0006 \\
姓名:劉予安、林宏信\\
課程名稱:機電整合與實習\\
授課老師:李政宏 博士\\
\end{flushleft}
\end{center}
\end{titlepage}
\section{單元名稱LED 特效燈}
\section{實習設備與材料}
\begin{table}[h!]
\begin{center}
\begin{tabular}{c|c|c|c}
\toprule
項次 & 品名 & 數量 \\
\midrule
1 & Arduino & 1 \\
2 & LED & 8 \\
3 & 按鈕 & 4 \\
\bottomrule
\end{tabular}
\end{center}
\end{table}
\section{實習步驟}
這次的實作是要製作一個可透過按鈕切換不同模式的特效燈,每個特效運用 8 個數位 LED 做出各式的效果。這個實作在一開始的設計就有一個需要注意之處:由於按鈕可能在任何時間被按下,然而一個模式跑的過程需要時間,若沒有特別設計,就會需要使用者按著按鈕直到一個模式的週期結束,而這是不佳的使用者體驗。因此我在此運用與第一次實作相同的技巧,用計時器來取代 delay達到非阻塞的設計。
首先是切換模式的函式,本函式運用靜態區域變數 last 儲存前一個狀態,在按下按鈕時,將其設為對應的模式並回傳。
\lstinputlisting[linerange={41-51}]{week4-4.ino}
接著即是程式的主迴圈,每次依據現在的模式,呼叫對應的函式。
\lstinputlisting[linerange={103-118}]{week4-4.ino}
接著是這個程式最特別之處call\_interval。這個函式會每隔 DELAY 的時間,執行傳入的函式指標 f。雖然目前是使用固定的 DELAY 常數,但這個函式可以透過多接受一個參數,來改變要等待的時間,就可以實現不同的模式跑不同的 delay。藉由 call\_interval 函式,各模式燈光控制的函式就可以簡化為一個單次執行的程式。
\lstinputlisting[linerange={26-33}]{week4-4.ino}
最後就是三種模式各別的控制函式,每個函式會接受一個 clk 或 value 的參數,用來代表目前執行到第幾次。首先是二進位模式,我使用右移運算元來將數字除二,接著再取餘,即可用一行程式碼做出二進位的燈光。
\lstinputlisting[linerange={57-61}]{week4-4.ino}
格雷碼則是先將輸入用格雷碼的公式運算後,代入前面的二進位程式。
\lstinputlisting[linerange={63-65}]{week4-4.ino}
最複雜的則是隨機點亮燈光的程式,由於一個燈光點亮後會持續 3 個週期因此需要用變數儲存每個燈的狀態。我運用一個陣列來存每個燈光剩餘的「血量」0 代表不亮,大於 0 則代表亮,每個週期會將亮著的燈血量減一。接著是隨機點亮的程式,使用 do while 迴圈,在選到沒亮的燈前,會不停抽選燈,將其點亮。
\lstinputlisting[linerange={71-95}]{week4-4.ino}
\section{問題與討論}
在 get\_mode 的地方仍然有個小 bug當按著比較前面的按鈕時同時按後面的按鈕會讓後面的按鈕觸發。如按著 B4 還沒放開時,同時按下 B1這時模式會被切為 1 號模式;但反之按著 B1 時按下 B4仍然會是 1 號模式。這是由於程式是由前往後檢查,找到一個按下的按鈕就會回傳。但由於只要按鈕按著就會不停的重設計數器,要等到放開所有按鈕,燈光才會開始動作,經過評估沒有必要設計更複雜邏輯來解決。
\section{心得與建議}
這次的實驗過程體驗到了一個不斷讓程式碼精進的過程,從一開始的原型開發,專注在功能的實作,接著不斷的簡化、重構程式碼,使其愈加精鍊,最後得到了相當簡潔的程式碼,這樣的過程給了我很大的成就感。
\pagebreak{}
\section{完整程式碼}
\lstinputlisting{week4-4.ino}
\end{document}

20
week3-2/week3-2.ino Normal file
View File

@@ -0,0 +1,20 @@
#define LED 7
#define pbOn A0
#define pbOff A1
void setup() {
pinMode(LED, OUTPUT);
pinMode(pbOn, INPUT);
pinMode(pbOff, INPUT);
}
void loop() {
if (!digitalRead(pbOff)) {
digitalWrite(LED, HIGH);
while (!digitalRead(pbOff));
}
else if (!digitalRead(pbOn)) {
digitalWrite(LED, LOW);
while (!digitalRead(pbOn));
}
}

32
week3-3/week3-3.ino Normal file
View File

@@ -0,0 +1,32 @@
#define LED 7
#define BUTTON A0
#define BLINK 8
#define DELAY 100
void setup() {
pinMode(LED, OUTPUT);
pinMode(BUTTON, INPUT);
pinMode(BLINK, OUTPUT);
}
bool mode() {
static bool last = HIGH;
static bool m = HIGH;
if (last && !digitalRead(BUTTON)) {
m = !m;
}
last = digitalRead(BUTTON);
return m;
}
void loop() {
static unsigned long clk = 0;
static bool blinkValue = HIGH;
digitalWrite(LED, mode());
if (millis() - clk > DELAY) {
blinkValue = !blinkValue;
clk = millis();
}
digitalWrite(BLINK, blinkValue);
delay(1);
}

17
week4-1/week4-1.ino Normal file
View File

@@ -0,0 +1,17 @@
const int LED[] = {11, 10, 9, 8, 7, 6, 5, 4};
void setup() {
for (int i = 0; i < 8; i++) {
pinMode(LED[i], OUTPUT);
digitalWrite(LED[i], HIGH);
}
}
void loop() {
static bool clk = 0;
for (int i = 0; i < 8; i++) {
digitalWrite(LED[i], i % 2 == clk);
}
delay(250);
clk = !clk;
}

21
week4-2/week4-2.ino Normal file
View File

@@ -0,0 +1,21 @@
const int LED[] = {11, 10, 9, 8, 7, 6, 5, 4};
void setup() {
for (int i = 0; i < 9; i++) {
pinMode(LED[i], OUTPUT);
digitalWrite(LED[i], HIGH);
}
}
void loop() {
for (int i = 0; i < 9; i++) {
digitalWrite(LED[i], LOW);
delay(100);
digitalWrite(LED[i-2], HIGH);
}
for (int i = 0; i < 8; i++) {
digitalWrite(LED[6-i], LOW);
delay(100);
digitalWrite(LED[8-i], HIGH);
}
}

25
week4-3/week4-3.ino Normal file
View File

@@ -0,0 +1,25 @@
const int LED[] = {11, 10, 9, 8, 7, 6, 5, 4};
void setup() {
for (int i = 0; i < 9; i++) {
pinMode(LED[i], OUTPUT);
digitalWrite(LED[i], HIGH);
}
}
void loop() {
for (int i = 0; i < 3; i++) {
digitalWrite(LED[3-i], LOW);
digitalWrite(LED[4+i], LOW);
delay(100);
digitalWrite(LED[3-i], HIGH);
digitalWrite(LED[4+i], HIGH);
}
for (int i = 0; i < 3; i++) {
digitalWrite(LED[i], LOW);
digitalWrite(LED[7-i], LOW);
delay(100);
digitalWrite(LED[i], HIGH);
digitalWrite(LED[7-i], HIGH);
}
}

118
week4-4/week4-4.ino Normal file
View File

@@ -0,0 +1,118 @@
#define DELAY 200
const int LED[] = {11, 10, 9, 8, 7, 6, 5, 4};
const int BUTTON[] = {A0, A1, A2, A3};
unsigned int clk = 0; // the counter in every mode
void setup() {
for (int i = 0; i < 8; i++) {
pinMode(LED[i], OUTPUT);
digitalWrite(LED[i], HIGH);
}
for (int i = 0; i < 4; i++) {
pinMode(BUTTON[i], INPUT);
}
}
/*
* Function: call_interval
* Call a function per DELAY
*
* f: the function being called
*
* Side effect: increase clk per DELAY
*/
void call_interval( void (*f)(unsigned int) ) {
static unsigned long checkpoint = 0;
if (millis() - checkpoint > DELAY) {
(*f)(clk);
clk += 1;
checkpoint = millis();
}
}
/*
Function: get_mode
Return the current mode from buttons input
Side effects: reset the clk on button pressed
*/
unsigned int get_mode() {
static int last = 0;
for (int i = 0; i < 4; i++) {
if (!digitalRead(BUTTON[i])) {
last = i + 1;
clk = 0;
return last;
}
}
return last;
}
/*
Function: binary
Display the LED as binary of clk
*/
void binary(unsigned int value) {
for (int i = 0; i < 8; i++) {
digitalWrite(LED[7 - i], !((value >> i) % 2));
}
}
void gray_code(unsigned int value) {
binary(value ^ (value >> 1));
}
/*
Function: random_popup
Randomly select LED. Each LED will be turned on for 3 clk
*/
void random_popup(unsigned int clk) {
static int hp[8];
// Reset hp on mode changing
if (clk == 0) {
for (int i = 0; i < 8; i++) {
hp[i] = 0;
}
}
for (int i = 0; i < 8; i++) {
// Decrease hp
if (hp[i] > 0) {
hp[i] -= 1;
}
digitalWrite(LED[i], !(hp[i]));
}
// randomly select LED which is not turned on
int choice;
do {
choice = random(8);
} while (hp[choice] > 0);
hp[choice] = 4;
}
void clear_all() {
for (int i = 0; i < 8; i++) {
digitalWrite(LED[i], HIGH);
}
}
void loop() {
const int mode = get_mode();
if (mode == 1) {
call_interval(binary);
}
if (mode == 2) {
call_interval(gray_code);
}
if (mode == 3) {
call_interval(random_popup);
}
if (mode == 4) {
clear_all();
}
delay(1);
}

45
week5-1/week5-1.ino Normal file
View File

@@ -0,0 +1,45 @@
const unsigned int scan[] = {5, 6, 7, 8};
#define DATA 10
#define LATCH 11
#define CLK 12
byte numbers[] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99,
0x92, 0x82, 0xF8, 0x80, 0x90
};
void setup() {
for (int i = 0; i < 4; i++) {
pinMode(scan[i], OUTPUT);
digitalWrite(scan[i], HIGH);
}
pinMode(DATA, OUTPUT);
pinMode(LATCH, OUTPUT);
pinMode(CLK, OUTPUT);
}
void setDigits(int number, int digits[4]) {
for (int i = 0; i < 4; i++) {
digits[i] = number % 10;
number /= 10;
}
}
void loop() {
static int number = 0;
static unsigned long checkpoint = 0;
static int digits[4];
for (int i = 0; i < 4; i++) {
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLK, MSBFIRST, numbers[digits[i]]);
digitalWrite(LATCH, HIGH);
digitalWrite(scan[i], LOW);
delay(1);
digitalWrite(scan[i], HIGH);
}
if (millis() - checkpoint > 200) {
number++;
setDigits(number, digits);
checkpoint = millis();
}
}

50
week5-3/week5-3.ino Normal file
View File

@@ -0,0 +1,50 @@
const unsigned int scan[] = {5, 6, 7, 8};
#define DATA 10
#define LATCH 11
#define CLK 12
const int date[] = {2022, 323};
byte numbers[] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99,
0x92, 0x82, 0xF8, 0x80, 0x90
};
void setup() {
for (int i = 0; i < 4; i++) {
pinMode(scan[i], OUTPUT);
digitalWrite(scan[i], HIGH);
}
pinMode(DATA, OUTPUT);
pinMode(LATCH, OUTPUT);
pinMode(CLK, OUTPUT);
}
void setDigits(int number, int digits[4]) {
for (int i = 0; i < 4; i++) {
digits[i] = number % 10;
number /= 10;
}
}
void show(int digits[4]) {
for (int i = 0; i < 4; i++) {
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLK, MSBFIRST, numbers[digits[i]]);
digitalWrite(LATCH, HIGH);
digitalWrite(scan[i], LOW);
delay(1);
digitalWrite(scan[i], HIGH);
}
}
void loop() {
static unsigned long checkpoint = 0;
static int digits[4];
static bool mode = false;
show(digits);
if (millis() - checkpoint > 500) {
mode = !mode;
setDigits(date[mode], digits);
checkpoint = millis();
}
}

42
week5-4/week5-4.ino Normal file
View File

@@ -0,0 +1,42 @@
const unsigned int scan[] = {5, 6, 7, 8};
#define DATA 10
#define LATCH 11
#define CLK 12
byte numbers[] = {
0xC0, 0xF9, 0xA4, 0xB0, 0x99,
0x92, 0x82, 0xF8, 0x80, 0x90
};
void setup() {
for (int i = 0; i < 4; i++) {
pinMode(scan[i], OUTPUT);
digitalWrite(scan[i], HIGH);
}
pinMode(DATA, OUTPUT);
pinMode(LATCH, OUTPUT);
pinMode(CLK, OUTPUT);
}
void show(int digits[10], int shift) {
for (int i = 0; i < 4; i++) {
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLK, MSBFIRST, numbers[digits[(shift + i) % 10]]);
digitalWrite(LATCH, HIGH);
digitalWrite(scan[3-i], LOW);
delay(1);
digitalWrite(scan[3-i], HIGH);
}
}
void loop() {
static unsigned long checkpoint = 0;
static int digits[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
static int count = 0;
show(digits, count);
if (millis() - checkpoint > 500) {
count--;
if (count < 0) count = 9;
checkpoint = millis();
}
}

90
week5-5/week5-5.ino Normal file
View File

@@ -0,0 +1,90 @@
const unsigned int scan[] = {5, 6, 7, 8};
const unsigned int LED[] = {9, 13, 2, 3};
const unsigned int BUTTON[] = {A0, A1};
#define DATA 10
#define LATCH 11
#define CLK 12
void setup() {
for (int i = 0; i < 4; i++) {
pinMode(scan[i], OUTPUT);
digitalWrite(scan[i], HIGH);
}
for (int i = 0; i < 4; i++) {
pinMode(LED[i], OUTPUT);
digitalWrite(LED[i], HIGH);
}
for (int i = 0; i < 2; i++) {
pinMode(BUTTON[i], INPUT);
}
pinMode(DATA, OUTPUT);
pinMode(LATCH, OUTPUT);
pinMode(CLK, OUTPUT);
}
int get_mode() {
static int last = 0;
for (int i = 0; i < 2; i++) {
if (!digitalRead(BUTTON[i])) {
last = i+1;
return last;
}
}
return last;
}
void show(byte digits[4]) {
for (int i = 0; i < 4; i++) {
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLK, MSBFIRST, ~digits[i]); // 低態觸發
digitalWrite(LATCH, HIGH);
digitalWrite(scan[3 - i], LOW); // 點亮一毫秒
delay(1);
digitalWrite(scan[3 - i], HIGH);
}
}
void mode_display() {
static byte digits[4]; // 所有數字的狀態
static byte p = 0; // 現在要改變的數字
static unsigned long checkpoint = 0;
show(digits);
if (millis() - checkpoint > 200) {
if (p == 4) {
// 所有 LED 皆點亮,重設所有值
p = 0;
for (int i = 0; i < 4; i++) digits[i] = 0;
}
digits[p] <<= 1; // 向左移位
digits[p] += 1; // 補一
if (digits[p] == 0xFF) {
// if current digit is full, change to the next one
p++;
}
checkpoint = millis();
}
}
void mode_led() {
static unsigned long checkpoint;
static int c = 0;
if (millis() - checkpoint > 200) {
for (int i = 0; i < 4; i++) {
digitalWrite(LED[i], i != c%4);
}
c++;
checkpoint = millis();
}
}
void loop() {
switch (get_mode()) {
case 1:
mode_display();
break;
case 2:
mode_led();
break;
}
}

BIN
week5-5/week5.pdf Normal file

Binary file not shown.

123
week5-5/week5.tex Normal file
View File

@@ -0,0 +1,123 @@
\documentclass[12pt,a4paper]{article}
\usepackage[top=0.75in, bottom=0.75in, left=0.75in, right=0.75in]{geometry}
\usepackage[utf8]{inputenc}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{xurl}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{booktabs}
\lstset{ % General setup for the package
language=C,
basicstyle=\footnotesize\ttfamily,
numbers=left,
numberstyle=\small,
frame=tb,
tabsize=4,
columns=fixed,
showstringspaces=false,
showtabs=false,
keepspaces,
commentstyle=\color{gray},
keywordstyle=\color{blue}
}
\usepackage{xeCJK}
\setCJKmainfont{TW-Kai}
\setCJKmonofont{Noto Sans Mono CJK TC}
\author{1082B0006 林宏信}
\title{}
\begin{document}
\begin{titlepage}
\begin{center}
\vspace*{1cm}
\Huge
\textbf{國立臺北科技大學}
\vspace{0.5cm}
\textbf{學生實習心得報告}
\vspace{1.5cm}
\textbf{}
\vspace*{\fill}
\begin{flushleft}
\large
學期110 學年度第 2 學期\\
科別:智慧自動化控制科 \\
班級:三年級 \\
學號1082B0004、1082B0006 \\
姓名:劉予安、林宏信\\
課程名稱:機電整合與實習\\
授課老師:李政宏 博士\\
\end{flushleft}
\end{center}
\end{titlepage}
\section{單元名稱:七段顯示器}
本次的主題是七段顯示器,七段顯示器本身只是七個 LED 燈排成數字 8 的形狀,透過不同的燈光組合來呈現不同的數字。然而由於有 7 個 LED 燈就會佔用 7 個接腳Arduino 上的接腳數量有限,就無法控制多個燈光。因此七段顯示器多半會搭配 74HC595 電晶體,它是 serial in - parallal out 的移位暫存器,即可只使用 clock、latch、serial in 三支腳位,輸出 8 位元的資料,連結到四個七段顯示器上,在使用 enable 控制哪幾個要亮起。
然而我們的開發板上只有一顆 74HC595因此只能夠同時儲存 8 位元的資料,因此不可能同時讓兩個七段顯示器亮兩個不同的數字,所以實際上是使用輪閃的方式,運用視覺暫留來製造同時亮的效果。
本次的實作題目是設計透過按鈕切換的兩個模式,第一個是控制四個七段顯示器從左到右照 a、b、c...g 的順序逐一亮起,第二則是控制 4 個 LED 的霹靂燈。
\section{實習設備與材料}
\begin{table}[h!]
\begin{center}
\begin{tabular}{c|c|c}
\toprule
項次 & 品名 & 數量 \\
\midrule
1 & Arduino & 1 \\
2 & 七段顯示器 & 4 \\
3 & 74HC595 & 1 \\
\bottomrule
\end{tabular}
\end{center}
\end{table}
\section{實習步驟}
一開始是先實作控制四個七段顯示器的函式我透過一個四個元素的陣列儲存四個七段顯示器分別要亮起的二進制編碼8 bits。show 函式會將四個七段顯示器輪流寫入資料、點亮 1ms、熄滅、換下一個七段顯示器。74HC595 透過 shiftOut 函式來寫入串列資料,在打開 latch 後,將 digits[i] 裡面的二進制編碼,先做 binary NOT 後,每個 clock 會推入一位元到 DATA pin 中,一共推入八個位元。此函式即可讓四個七段顯示器顯示任意的四個數字,但必須持續呼叫才會亮,因此主函式必須採用非阻塞寫法,所有的函式也不能有 delay。
\lstinputlisting[linerange={36-45}]{week5-5.ino}
再來是模式一的函式,此程式會每 200ms 將現在指定的數字往左移位並補一,即可做出燈光逐一亮起的效果。當一個七段顯示器的所有 LED 皆亮起時,則將 p 移至下一個數字。當四個數字都已被點亮,則重置所有 LED。
\pagebreak
\lstinputlisting[linerange={47-66}]{week5-5.ino}
至於模式二則是一個相當簡單的輪閃程式我用硬體接線來實作老師要求的特殊順序1、3、2、4。
\lstinputlisting[linerange={68-78}]{week5-5.ino}
其餘模式切換與主函式都與上一次報告類似,便不加贅述。
\section{問題與討論}
這次由於 74HC595 只有一顆的緣故,是使用輪閃的方式來實作,然而過程中可以觀察到,眼睛雖然看不出有閃,但是亮度跟持續亮著有差別,若今天要控制的是十六個七段顯示器,輪閃造成的亮度損失可能會相當嚴重。因此我想更好的做法,應為每個七段顯示器搭配一個 74HC585再透過多工器來將 clock、latch、serial in 接到每個 74HC595 上,這樣即可做到輪流更新、同時亮起,在沒有更新數值的時候也不需時時更新 74HC595更重要的不會有亮度減低的不良影響。
\section{心得與建議}
這次學習到了如何操作七段顯示器及 74HC595一開始沒有搞清楚串列輸入、輪閃等等的性質遇到了一些奇怪的錯誤在一次次嘗試中才慢慢理解實際運作原理。在幫助同學的過程中發現大部分人都是卡在「輪閃」這個點上我自己猜想是因為不了解 shiftOut 這個陌生指令究竟做了什麼,加上有多層的迴圈,邏輯確實是複雜了些。也許需要讓大家更了解程式各段的架構、邏輯究竟代表什麼,當然也需要搭配每個人的實作,從錯誤中去理解運作原理,但不能只是「程式能動就好」,而必須能解釋程式為什麼能動,才是真正的學會。
\pagebreak{}
\section{完整程式碼}
\lstinputlisting{week5-5.ino}
\end{document}