2012/02/21

Arduino練習:seven-segment display七段顯示器與時鐘

之前用LED製作呼吸燈霹靂車燈以揚聲器演奏青花瓷,這篇要使用seven-segment display七段顯示器製作時鐘。

首先使用Arduino的8個腳位,直接控制七段顯示器,讓七段顯示器從0數到9。然後改用4511控制七段顯示器,就只需要4個腳位即可。然後再加上三個七段顯示器、再加上三顆4511,製作時鐘。

電路圖(Fritzing格式)與程式原始碼,可在此下載

開始吧,先直接以Arduino的腳位控制,讓七段顯示器亮起來吧。

注意:我使用的是共陰極七段顯示器,之後的4511不能搭配共陽極。

共陰極七段顯示器的腳位圖。


電路圖:

左方是Arduino的腳位,右方是七段顯示器的腳位。連接時,都加上一個220 ohm電阻。
2連到7(A)。
3連到6(B)。
4連到4(C)。
5連到2(D)。
6連到1(E)。
7連到9(F)。
8連到10(G)。
9連到5(DP)。

從Arduino的GND接到麵包版上,讓七段顯示器的3與8接地。



接好後,先寫個小程式測試看看,讓七段顯示器閃爍。
#define NUM 8
int pins[NUM] = {2, 3, 4, 5, 6, 7, 8, 9};

void setup() {               
  for(int i = 0; i < NUM; i++){
    pinMode(pins[i], OUTPUT);   
  }
}

void loop() {
  for(int i = 0; i < NUM; i++){
    digitalWrite(pins[i], HIGH);
  }
  delay(1000);
  for(int i = 0; i < NUM; i++){
    digitalWrite(pins[i], LOW);
  } 
  delay(1000);
}



其實就跟閃爍單個LED一樣,只不過現在要閃爍8個LED罷了。

接下來,該讓七段顯示器作正事了,也就是顯示數字,底下程式碼要從0數到9。

首先要定義,顯示各數字時,要點亮、熄滅七段顯示器的哪些部分。

#define NUM 8
int pins[NUM] = {2, 3, 4, 5, 6, 7, 8, 9};

// 底下就是顯示某數字時,該點亮熄滅哪些腳位的定義,
// data是個二維陣列,其中10代表有10個數字(從數字0、1、2、到9),
// 然後,每個數字有8個boolean值,
// 依序代表該不該點亮七段顯示器的A、B、C、D、E、F、G、DP。
// 這部份的資料,依序對應上面pins陣例裡的腳位順序,
// 這裡的對應關係,要按照先前的接線予以定義。
// t代表true,要點亮,
// f代表false,不要點亮。
#define t true
#define f false
boolean data[10][NUM] = {
  {t, t, t, t, t, t, f, f}, // 0
  {f, t, t, f, f, f, f, f}, // 1
  {t, t, f, t, t, f, t, f}, // 2
  {t, t, t, t, f, f, t, f}, // 3
  {f, t, t, f, f, t, t, f}, // 4
  {t, f, t, t, f, t, t, f}, // 5
  {t, f, t, t, t, t, t, f}, // 6
  {t, t, t, f, f, f, f, f}, // 7
  {t, t, t, t, t, t, t, f}, // 8
  {t, t, t, t, f, t, t, f}, // 9
};

void setup(){               
  for(int i = 0; i < NUM; i++){
    pinMode(pins[i], OUTPUT);   
  }
}

// 把七段顯示器的顯示函式獨立出來。
// 輸入數字n(從0到9)
// 根據數字n,就可從data[n]找出七段顯示器的點亮、熄滅資料,
// 然後用迴圈,逐一設定8個腳位的HIGH或LOW,
// 也就等於點亮、熄滅那8個部分
void writeNumber(int n){
  for(int i = 0; i < NUM; i++){
    digitalWrite(pins[i], data[n][i] == t ? HIGH : LOW);
  }
}

// 寫好writeNumber()後,loop()就簡單了。
// 迴圈從0到9,以七段顯示器顯示該數字,中間延遲1秒。
// 也就是說,會從0數到9,然後再從頭從0開始數。
void loop(){
  for(int n = 0; n <= 9; n++){
    writeNumber(n);
    delay(1000);
  }
}


以4511控制:

上頭直接以Arduino板的8個腳位控制七段顯示器,底下改以4511控制,以4個腳位。(若以74HC595,就能以3個腳位控制8個輸出。之前的霹靂車燈以2顆74HC595控制16個LED。)

注意,4511必須跟共陰極的七段顯示器搭配。



4511的腳位說明:

7(input A)、1(input B)、2(input C)、6(input D),4個輸入腳位,從0(0x0000)到9(0x1001),若超過9,那麼7個輸出全部會是LOW,也就是都不亮。

3(lamp test),若為LOW,那麼輸出腳位全部為HIGH,不管輸入腳位的狀態。

4(ripple blanking),當lamp test為HIGH時,若此腳位為LOW,那麼輸出腳位全部為LOW,不管輸入腳位的狀態。

5(enable/store input),若為LOW,輸出腳位隨著輸入腳位而改變,若為HIGH,輸出腳位的狀態會被鎖住不變。

13(a)、12(b)、11(c)、10(d)、9(e)、15(f)、14(g),7個輸出腳位,控制七段顯示器。

8(VSS),接地。

16(VDD),可接+3~15V。

利用4511的ABCD四個腳位輸入,控制七段顯示器的abcdefg。


電路圖:

從Arduino板接5V與GND到麵包板。

4511的腳位接線:
16(VDD)接5V。
8(VSS)接地。
3(lamp test)接5V,本例不使用此功能。
4(ripple blanking)接5V,本例不使用此功能。
5(enable/store input)接地,本例不使用此功能。

13(a)、12(b)、11(c)、10(d)、9(e)、15(f)、14(g),7個輸出腳位,接到七段顯示器相對應的腳位。
7(input A)、1(input B)、2(input C)、6(input D),4個輸入腳位,接到Arduino板的腳位2、3、4、5。

七段顯示器的腳位5(DP)接地,本例不使用小數點。(其實之前從0數到9時,也不需要使用小數點。)
共陰極七段顯示器的腳位3與8要接地。

下圖先連接4511與七段顯示器之間的接線。



然後是4511的7(input A)、1(input B)、2(input C)、6(input D),4個輸入腳位,依序接到Arduino板的腳位2、3、4、5。。



接下來就是程式碼了,從0數到9。

// 以Arduino板的4個腳位2、3、4、5控制,
// 控制4511的輸入腳位7(A)、1(B)、2(C)、6(D)。
int pins[4] = {2, 3, 4, 5};

void setup(){
  for(int i = 0; i < 4; i++){
    pinMode(pins[i], OUTPUT);   
  }
}

// 以迴圈依序設定4個腳位。
// 參數n,其第0個bit,要輸出到4511的腳位7(input A),
// 它接到Arduino板的腳位2,也就是pins[0]。
// 而取得n的第i個bit的方式是,
// 先以>>運算子把n向右位移i個bit,然後與0x01進行作&運算。
// 其餘bit同理。

void writeDigit(int n){
  for(int i = 0; i < 4; i++){
    digitalWrite(pins[i], (n >> i) & 0x01);
  }
}

// 寫好writeDigit()後,loop()就簡單了。
// 以迴圈依序顯示0到9,中間延遲1秒。
void loop(){
  for(int n = 0; n <= 9; n++){
    writeDigit(n);
    delay(1000);
  }
}

從0數到9的影片在此

時鐘:

接下來,要製作時鐘,可以顯示幾點、幾分,總共需要4個七段顯示器,以及4個4511。

電路圖:

延續前面的電路,加上3個4511,把短的線接一接。
16(VDD)接5V。
8(VSS)接地。
3(lamp test)接5V,本例不使用此功能。
4(ripple blanking)接5V,本例不使用此功能。
5(enable/store input)接地,本例不使用此功能。

從右到左的4511,分別將要控制時鐘的分鐘個位數、分鐘十位數、小時個位數、小時十位數。

注意,小時十位數(最左邊)的4511,其腳位12(b)接電阻後接地,這是因為,小時十位數的七段顯示器,只會出現0、1、2這三個數字,你可以往前看看ABCD與abcdefg的對應表,若七段顯示器只需顯示0、1、2時,其b腳位只能為HIGH,所以這裡就把4511的12(b)腳位接地不用。



然後加上3個七段顯示器,把短的線以及電阻接一接。(我220 ohm電阻不夠了,有些改成別的差不多的。)
七段顯示器的腳位5(DP)接地,本例不使用小數點。
共陰極七段顯示器的腳位3與8要接地。

注意,小時十位數(最左邊)的七段顯示器,其腳位6(b)接地,理由之前說過了。




然後,4511與七段顯示器之間的連接線。
4511的13(a)、12(b)、11(c)、10(d)、9(e)、15(f)、14(g),7個輸出腳位,接到七段顯示器相對應的腳位。小心慢慢接。



然後,4511與Arduino板的連接線。

分鐘個位數的4511,7(input A)、1(input B)、2(input C)、6(input D),4個輸入腳位,依序接到Arduino板的腳位2、3、4、5。(這個之前應該就接好了。)

分鐘十位數的4511,7(input A)、1(input B)、2(input C)、6(input D),4個輸入腳位,依序接到Arduino板的腳位6、7、8、9

小時個位數的4511,7(input A)、1(input B)、2(input C)、6(input D),4個輸入腳位,依序接到Arduino板的腳位10、11、12、13

小時十位數的4511,7(input A)、1(input B)、2(input C)、6(input D),4個輸入腳位,依序接到Arduino板的腳位A0、A1、接地、接地

注意,小時十位數的4511的2(input C)、6(input D),接地。這是因為,小時十位數的七段顯示器,只會出現0、1、2這三個數字,你可以往前看看ABCD與abcdefg的對應表,若4511只需輸入0、1、2時,其C、D輸入腳位一定是LOW,所以這裡此兩腳位接地不用。




接下來,先寫個測試程式,測試看看Arduino是否正確控制4511的輸入腳位,測試七段顯示器是否能正常顯示每個數字。

// 定義腳位
const int minute_0[4] = {2, 3, 4, 5}; // 分鐘個位數
const int minute_1[4] = {6, 7, 8, 9}; // 分鐘十位數
const int hour_0[4] = {10, 11, 12, 13}; // 小時個位數
const int hour_1[2] = {A0, A1}; // 小時十位數

// 將數字n,輸出到腳位pins,bit_count是腳位的數量
void writeDigit(const int n, const int *pins, const int bit_count){
  for(int i = 0; i < bit_count; i++){
    digitalWrite(pins[i], (n >> i) & 0x01);
  }
}

// 在setup()裡初始化,讓四個七段顯示器都顯示數字0。
void setup(){

  for(int i = 0; i < 4; i++){
    pinMode(minute_0[i], OUTPUT);
    pinMode(minute_1[i], OUTPUT);
    pinMode(hour_0[i], OUTPUT);
  }
  pinMode(hour_1[0], OUTPUT);
  pinMode(hour_1[1], OUTPUT);
 
  writeDigit(0, minute_0, 4);
  writeDigit(0, minute_1, 4);
  writeDigit(0, hour_0, 4);
  writeDigit(0, hour_1, 2);
}

// 從0數到9,其中小時十位數只能顯示0、1、2。
void loop(){
  for(int i = 0; i <= 9; i++){
    writeDigit(i, minute_0, 4);
    writeDigit(i, minute_1, 4);
    writeDigit(i, hour_0, 4);
    writeDigit(i % 3, hour_1, 2);
    delay(1000);
  }
}

成功的話,右邊三個七段顯示器就會從0數到9,最左邊的會顯示0、1、2。


接下來,要製作時鐘,從0000、0001、一直到2359,然後回到0000。

程式碼:
const int minute_0[4] = {2, 3, 4, 5};
const int minute_1[4] = {6, 7, 8, 9};
const int hour_0[4] = {10, 11, 12, 13};
const int hour_1[2] = {A0, A1};

void writeDigit(const int n, const int *pins, const int bit_count);
void setTime(unsigned long h, unsigned long m, unsigned long s);

void setup(){
  Serial.begin(115200);
  for(int i = 0; i < 4; i++){
    pinMode(minute_0[i], OUTPUT);
    pinMode(minute_1[i], OUTPUT);
    pinMode(hour_0[i], OUTPUT);
  }
  pinMode(hour_1[0], OUTPUT);
  pinMode(hour_1[1], OUTPUT);
 
  setTime(7, 32, 0); // 在程式裡寫死某時間,7時32分0秒
  // 之後我們會從外界設定時間
}

void writeDigit(const int n, const int *pins, const int bit_count){
  for(int i = 0; i < bit_count; i++){
    digitalWrite(pins[i], (n >> i) & 0x01);
  }
}

// 代表一天的millisecond,在數字後加上UL,代表是unsigned long
// 若不加上UL,會發生溢位overflow
// 24*60*60 = 86400,大於int或unsigned int(2 bytes)的範圍,
// 若不加上UL,24*60*60,正確應該是0b10101000110000000,
// 溢位後,變成0b0101000110000000,也就是20864。
#define MILLISECOND_OF_ONE_DAY (24UL * 60UL * 60UL * 1000UL)

// 這會是目前顯示在七段顯示器上的時間,單位為millisecond
unsigned long currentTime;

// 用來記住上次更新currentTime時,是在何時
unsigned long previousTimeMark;

// 設定顯示時間的函式,h小時、m分鐘、s秒
void setTime(unsigned long h, unsigned long m, unsigned long s){
  currentTime = ((h * 60 + m) * 60 + s) * 1000;
  previousTimeMark = millis();
  updateTime(0);
}

// 負責更新顯示時間的函式,
// 參數timePassed代表,距離上次更新,經過了多少millisecond
void updateTime(const unsigned long timePassed){
  unsigned long h;
  unsigned long m;
  unsigned long s;

  // 加上經過的時間後,若大於一天,就減去。
  currentTime += timePassed;
  if(currentTime >= MILLISECOND_OF_ONE_DAY){
    currentTime -= MILLISECOND_OF_ONE_DAY;
  }

  // currentTime記錄的是millisecond,
  // 底下算出小時、分鐘、秒
  s = currentTime / 1000;
  m = s / 60;
  s = s % 60;
  h = m / 60;
  m = m % 60;

  // 顯示小時跟分鐘
  writeDigit(m % 10, minute_0, 4);
  writeDigit(m / 10, minute_1, 4);
  writeDigit(h % 10, hour_0, 4);
  writeDigit(h / 10, hour_1, 2);
}

void loop(){
  // millis()會回傳,這支程式開始執行後,所經過的時間,單位是millisecond。
  unsigned long now = millis();

  // 減去previousTimeMark上次更新顯示時間的時間,
  // 若大於1秒,就呼叫updateTime()更新,
  // 傳入距離上次更新所經過的時間
  if(now - previousTimeMark > 1000){
    updateTime(now - previousTimeMark);
    previousTimeMark = now;
  }
}

成功後,就可以看到下圖了。


不過,上面的程式碼,在程式碼寫死起始時間,這可不行啊,底下修改後,透過序列埠,從外界(電腦)進行時間設定。

在setup()裡要記得加入Serial.begin(115200);。

int b[4];
int len = 0;
void loop(){
  unsigned long now = millis();
  if(now - previousTimeMark > 1000){
    updateTime(now - previousTimeMark);
    previousTimeMark = now;
  }
 
  int btemp = Serial.read();
  if(btemp != -1){
    if(0x30 <= btemp && btemp <= 0x39){
      b[len] = btemp - 0x30;
      len++;
      if(len == 4){
        setTime(b[0] * 10 + b[1], b[2] * 10 + b[3], 0);
        len = 0;
      }
    }
    else{
      len = 0;
    }
  }
}

利用Arduino軟體開發環境的Tools-Serial Monitor,可以透過序列埠、傳送資料給Arduino板子,以腳位0(RX)接收。

呼叫Serial.read()讀取1個byte,放在btemp裡,若無資料,則btemp為-1,若有資料(傳來的會是ASCII碼、0x30是數字0、0x39是數字9),放進b陣列裡存起來,以len記錄收到了多少個byte。收到4個數字後,把前面兩個數字當做小時、後面兩個數字當做分鐘,進行時間設定。

若收到數字以外的資料,重置len為0,表示之前若有收到資料,通通捨棄。

輸入2302,代表要把時間設定為23時02分。


完成囉。

改進的地方,也可以加入設定時間的開關或按鈕,譬如說,某個按鈕按一下會增加一分鐘,另一個按鈕按一下會增加一小時,之類的。

改進的地方,右邊數來第二顆4511,跟Arduino板之間的接線,其實3條即可(因為它只需顯示0到5)。

改進的地方,這個時鐘只顯示小時與分鐘,沒有秒,可加入兩顆LED,每一秒閃爍一下。好像有那種一組裡有四個七段顯示器,並且中間有兩顆LED的,但我沒找到,而且那種一組四個七段顯示器的產品,要用掃描的方式驅動。


參考資料:

42 comments:

  1. 請問一下,我程式碼修改過了,但是沒辦法外界修改時間!!請問要怎麼去修改

    ReplyDelete
  2. 我是有照大大的程式碼去做:
    const int minute_0[4] = {2, 3, 4, 5};
    const int minute_1[4] = {6, 7, 8, 9};
    const int hour_0[4] = {10, 11, 12, 13};
    const int hour_1[2] = {A0, A1};

    void writeDigit(const int n, const int *pins, const int bit_count);
    void setTime(unsigned long h, unsigned long m, unsigned long s);

    void setup(){
    Serial.begin(115200);
    int b[4];
    int len = 0;
    for(int i = 0; i < 4; i++){
    pinMode(minute_0[i], OUTPUT);
    pinMode(minute_1[i], OUTPUT);
    pinMode(hour_0[i], OUTPUT);
    }
    pinMode(hour_1[0], OUTPUT);
    pinMode(hour_1[1], OUTPUT);

    setTime(8, 35, 0); // 在程式裡寫死某時間,7時32分0秒
    // 之後我們會從外界設定時間
    int btemp = Serial.read();
    if(btemp != -1){
    if(0x30 <= btemp && btemp <= 0x39){
    b[len] = btemp - 0x30;
    len++;
    if(len == 4){
    setTime(b[0] * 10 + b[1], b[2] * 10 + b[3], 0);
    len = 0;
    }
    }
    else{
    len = 0;
    }
    }
    }



    void writeDigit(const int n, const int *pins, const int bit_count){
    for(int i = 0; i < bit_count; i++){
    digitalWrite(pins[i], (n >> i) & 0x01);
    }
    }

    // 代表一天的millisecond,在數字後加上UL,代表是unsigned long
    // 若不加上UL,會發生溢位overflow
    // 24*60*60 = 86400,大於int或unsigned int(2 bytes)的範圍,
    // 若不加上UL,24*60*60,正確應該是0b10101000110000000,
    // 溢位後,變成0b0101000110000000,也就是20864。
    #define MILLISECOND_OF_ONE_DAY (24UL * 60UL * 60UL * 1000UL)

    // 這會是目前顯示在七段顯示器上的時間,單位為millisecond
    unsigned long currentTime;

    // 用來記住上次更新currentTime時,是在何時
    unsigned long previousTimeMark;

    // 設定顯示時間的函式,h小時、m分鐘、s秒
    void setTime(unsigned long h, unsigned long m, unsigned long s){
    currentTime = ((h * 60 + m) * 60 + s) * 1000;
    previousTimeMark = millis();
    updateTime(0);
    }

    // 負責更新顯示時間的函式,
    // 參數timePassed代表,距離上次更新,經過了多少millisecond
    void updateTime(const unsigned long timePassed){
    unsigned long h;
    unsigned long m;
    unsigned long s;

    // 加上經過的時間後,若大於一天,就減去。
    currentTime += timePassed;
    if(currentTime >= MILLISECOND_OF_ONE_DAY){
    currentTime -= MILLISECOND_OF_ONE_DAY;
    }

    // currentTime記錄的是millisecond,
    // 底下算出小時、分鐘、秒
    s = currentTime / 1000;
    m = s / 60;
    s = s % 60;
    h = m / 60;
    m = m % 60;

    // 顯示小時跟分鐘
    writeDigit(m % 10, minute_0, 4);
    writeDigit(m / 10, minute_1, 4);
    writeDigit(h % 10, hour_0, 4);
    writeDigit(h / 10, hour_1, 2);
    }

    void loop(){
    // millis()會回傳,這支程式開始執行後,所經過的時間,單位是millisecond。
    unsigned long now = millis();

    // 減去previousTimeMark上次更新顯示時間的時間,
    // 若大於1秒,就呼叫updateTime()更新,
    // 傳入距離上次更新所經過的時間
    if(now - previousTimeMark > 1000){
    updateTime(now - previousTimeMark);
    previousTimeMark = now;
    }
    }
    這是我看您的去修改,發現板子還是的數字是固定的,沒辦法用COM改想要的數字,只能用裡面的程式碼固定

    ReplyDelete
    Replies
    1. 嗯,沒改對吧, Serial.read()的部份應放在loop()裡。
      完整的程式碼,請到
      https://github.com/yehnan/arduino_practices/tree/master/Clock
      下載。

      Delete
    2. Anonymous2/6/22 00:13

      請問要把無災害天數告示牌的日期顯示變成7段顯示器,和每天加一天的數字功能要買那些材料

      Delete
  3. 大大,我試過了,發現還是一樣沒辦法去改...可以交一下怎麼修改嗎??

    ReplyDelete
    Replies
    1. > 可以交一下怎麼修改嗎??
      這篇文章的內容,就是使用四個七段顯示器製作時鐘,並透過序列埠設定時間。

      若碰到問題,請說明。

      Delete
  4. 請問一下我是剛進來的的新手 怎麼使用按鈕控制 7段顯示上數 之後在伺服馬達震盪反覆啊

    我只打出7段顯示上數 之後就不知道怎麼用按鈕和馬達

    ReplyDelete
    Replies
    1. > 7段顯示上數
      什麼意思?

      已經學會按鈕和馬達了嗎?若尚未,請參閱其他文章,
      http://yehnan.blogspot.tw/2012/02/arduino_21.html
      譬如:
      Arduino練習:以開關切換LED明滅狀態
      Arduino練習:seven-segment display七段顯示器與時鐘
      Arduino練習:Simon Says請你跟我這樣做
      Arduino練習:伺服馬達以Tower Pro SG90為例

      也可參閱拙作《Arduino輕鬆入門:範例分析與實作設計》,
      http://yehnan.blogspot.tw/2014/02/arduino_21.html

      學會操控各個零件之後,接著就是程式的部份。

      Delete
  5. 嗯,電壓電流都大,不是該由Arduino板子提供的程度。
    要另外準備電源,還有驅動器,例如電晶體、達靈頓、或七段顯示器的解碼器,
    Arduino與七段顯示器之間插入驅動器,而驅動器要由外部電源提供電力,
    最後記得共同接地。

    ReplyDelete
  6. 葉老師請問您,我現在要驅動+9V才會亮的共陽七段(編號:BA-S2320SBAW),
    我使用ULN2003+74LS47來接七段,
    請問這時候ARDUINO的腳位(2,3,4,5)是接ULN2003的1B~4B嗎?
    然後ULN2003的1C~4C是接74LS47的ABCD嗎?
    然後外部供電的+9V及GND該接哪些呢?
    有點頭大,還請老師指導,謝謝您。

    ReplyDelete
  7. 抱歉葉老師,外部電源接線的方式,大概清楚了。
    不過出現了一堆奇怪形狀的七段。
    我猜大概是共陽七段跟你的範例不同的關係吧?

    您的這段程式碼,我真的看不懂,
    for(int i = 0; i < 4; i++){
    digitalWrite(pins[i], (n >> i) & 0x01);
    右移&oxo1,實在太深奧了。
    請問改共陽,要如何修改呢?

    ReplyDelete
  8. 線路大概是:
    arduino四個腳位接到74LS47的ABCD,
    74LS47的a~g,接到ULN2003的1B~7B,
    ULN2003的1C~7C,接到七段顯示器的a~-g,
    七段顯示器的共陽極,接到9V~12V。

    ReplyDelete
  9. 這個範例只有一個七段顯示器,
    writeDigit負責控制那個七段顯示器,參數是int n,代表數字0~9。
    我用4511,以4個腳位控制七段顯示器的8個腳位(加小數點),

    int n是16位元,
    如果傳入9的話,n會是0000 1001,
    如果傳入6的話,n會是0000 0110,
    所以只需要取出其中4個位元,
    所以for迴圈的i從0到3,

    假定n是9,
    起初i是0,n向右移0個位置,然後與0x01作bitwise AND,取出來是1,設給腳位,
    然後i是1,n向右移1個位置,然後與0x01作bitwise AND,取出來是0,設給腳位,
    然後i是2,n向右移2個位置,然後與0x01作bitwise AND,取出來是0,設給腳位,
    最後i是3,n向右移3個位置,然後與0x01作bitwise AND,取出來是1,設給腳位。

    ReplyDelete
  10. 我大概搞清楚了。
    我用很粗淺的計概概念來理解,
    這個for(int i = 0; i < 4; i++){
    digitalWrite(pins[i], (n >> i) & 0x01);
    的作用就是將n化為二進位,然後依序由右到左取第i位的字串值給對應腳位。
    是這個意思吧?
    真的非常感謝葉老師不厭其煩、逐步詳細的指點教導我這個陌生的網友。
    您上課一定講解得更詳盡。
    很希望能有機會能實際地接受您的教導,謝謝您。

    ReplyDelete
  11. > 是這個意思吧?
    yes.

    ReplyDelete
  12. 抱歉葉老師,再請教您一個問題:
    我想在一段時間內,讓15個不同的LED燈在不同時間點亮。
    例如: 以下時間格式為 分:秒
    0:01 亮1號燈
    0:03.2 亮3號燈
    0:04.3 亮2號燈
    0:05.2 亮4號燈
    0:05.7 亮1號燈
    0:06.3 亮5號燈+亮3號燈
    0:07.5 亮6號燈+亮7號燈
    0:08.0 亮3號燈
    .....
    這樣的點燈動作共有數百個
    請問有類似的範例可供參考嗎?
    或者我該下怎樣的關鍵字去搜尋類似的題目呢?
    再次謝謝老師的指導,感謝您。

    ReplyDelete
  13. 如果有規則,就要寫程式描述出來。
    沒有的話,只能建表,例如
    typedef struct{
    int t;
    int pin;
    boolean on;
    } Data;

    Data data[] = {
    {50, 1, HIGH};
    {150, 3, HIGH};
    {450, 2, HIGH};
    };
    int idx = 0;
    類似這樣的東西吧,然後寫程式處理,例如

    unsigned long time_old;
    開始處理時,time_old = millis(); 記錄起始時間,
    然後在處理函式裡,根據時間順序,點亮該亮起的LED。
    void blink(){
    unsigned long t_now = millis();
    if(t_now + data[idx].t > time_old){
    digitalWrite(data[idx].pin, data[idx].on);
    }
    }

    上面程式不全,只有極少部份的功能,僅供參考。

    要不然就要去找找,有沒有別人寫好的程式庫。

    ReplyDelete
  14. 謝謝老師,這裡算是沒有規則的點燈。
    因此我搜尋該下哪些關鍵字呢?
    arduino+資料表+?+?
    也謝謝老師的範例,甘溫。

    ReplyDelete
  15. 關鍵字............我也不知道耶。

    ReplyDelete
  16. 你圖片上的是共陽 不是共陰

    ReplyDelete
  17. 我看錯了 業老師 我問一下,如果加兩個按鈕,一個是時,一個是分
    意思是不用重程式改時間,用按鈕改時間?

    ReplyDelete
    Replies
    1. 嗯,偵測按鈕是否按下,得知想增加時還是分,然後傳入適當地參數給updateTime

      Delete
    2. 程式碼要怎麼打出來 我還是個學生

      Delete
    3. 用手打。
      我不是個學生。

      Delete
  18. 可以不加電阻,直接寫pull-up方式嗎?

    ReplyDelete
    Replies
    1. 不懂。
      pull-up什麼?

      這裡是用OUTPUT模式。

      Delete
    2. Arduino 的設計者考慮 GPIO pin 可能拿來做數位輸入(Digital Input),
      所以每支 pin 內部都已經內建了一個 20K歐姆的上拉(Pull-up)電阻,
      你只要這樣:
      pinMode(pin, INPUT_PULLUP);
      這樣就可以啟動內建的上拉電阻, 省去外接一個上拉電阻的麻煩 :-)
      此時, 如果要測試按鈕,
      則只要拿一條杜邦線或任意的導線從 GND 拉出然後碰觸 pin,
      這樣就等於按下按鈕, digialRead(pin) 就會讀到 LOW 的值.

      轉自http://www.arduino.cn/thread-13186-1-1.html

      Delete
  19. Anonymous9/6/17 01:41

    請問如何控制一個4位數七段
    一個數字閃爍 其他恆亮呢?
    謝謝您!

    ReplyDelete
    Replies
    1. Anonymous9/6/17 01:48

      我想達到的效果是這樣的...
      按鍵A,B 更改閃爍位置顯示的數字
      按鍵C 更改閃爍位置

      目前卡關在 如何只讓一個數字閃爍而其他的恆亮
      再次謝謝您!

      Delete
    2. 請自己動手寫寫看。

      使用Timer程式庫,http://yehnan.blogspot.tw/2012/03/arduino.html,

      #include 小於Timer.h大於
      Timer timer;
      boolean b;
      void blinkDigit(){
      b = !b;
      if(b){
      顯示某個位數
      }
      else{
      不顯示某個位數,也就是讓它全部熄滅
      }
      }
      void setup(){
      timer.every(300, blinkDigit);
      }
      void loop()}{
      timer.update();
      }

      大概是這樣吧,沒真的跑過。

      Delete
    3. Anonymous11/6/17 04:53

      謝謝您的建議!
      之後想到的方法是利用timer.every計數
      int cnt, HiTime, LoTime;
      if(掃描到閃爍位置 && cnt>=Hitime){
      閃爍位置不顯示
      if(cnt>=LoTime){
      cnt=0;
      }
      }
      else{
      閃爍位置顯示
      }
      另想請問如果stop後要重新啟動timer
      只要直接呼叫every就可以了嗎?
      謝謝!

      Delete
    4. 也可以。

      對。

      Delete
  20. Anonymous13/6/17 03:11

    請問我要加快時鐘的頻率,我該在哪邊修改呢?

    ReplyDelete
    Replies

    1. if(now - previousTimeMark > 1000){
      updateTime(now - previousTimeMark);
      previousTimeMark = now;
      }

      例如改成
      if(now - previousTimeMark > 100){
      updateTime((now - previousTimeMark) * 1.5);
      previousTimeMark = now;
      }

      試試看吧。

      Delete
  21. 葉難 老師

    想請問如何將arduino量測到的分壓輸出在七段顯示器上

    ReplyDelete
    Replies
    1. 用analogRead會讀到0~1023吧,轉成相對應的電壓值,譬如3.21,
      取出每一個位數,然後分別顯示。

      Delete
  22. 請問要如何改用四合一七段顯示器

    ReplyDelete