2012年12月2日 星期日

IO 擴充應用 - 使用 74HC595 (三態輸出八位元移位暫存器 IC)

需要此 IC 的話請到露天賣場訂購:SN74HC595 (IO 擴充 IC) - 三態輸出八位元移位暫存器 IC


SN74HC595N 是 TI 公司生產的 IC,具有三態輸出八位元移位暫存器通常使用在數位 IO 數目的擴充上,一串最多串接到 15顆 (也就是可達 120 個 IO 擴充),使用的方法就是把 74HC595 當作是 SPI Slave 來做使用。


由於 SPI 週邊的基本運作就是移位暫存器,所以我們可以簡單的使用八位元移位暫存器和輸出鎖存器 (output latch) 來擴充輸出埠,74HC595 家族就是可用來達成這目的晶片。


使用前請先看一下:
規格表說明

使用 74xx595 做數位 IO 接腳擴充時,接腳的名稱會依照製作廠商的不同而有不同的命名方式,較常用的有下面這兩種命名方式:

接腳代號 (type 1) 接腳說明 (type 1) 接腳代號 (type 2) 接腳說明 (type 2)
Q7'  (9) serial data output QH'  (9)serial data output
MR (10) Master Reset (Active Low) SRCLR  (10)Shift register CLeaR
SH_CP (11) shift register clock input SRCLK  (11)Shift Register CLocK input
ST_CP (12) storage register clock input RCLK  (12)storage Register CLocK input
OE (13) output enable input (Active Low) OE  (13) Output Enable
DS (14) serial data input SER  (14)SERial data input

表格裡面的接腳是比較重要的,名稱不同但功能是一樣的,使用時可做為對照。


控制流程:

大略講解一下資料輸入與輸出的流程:假設我們要控制輸出接腳 ( Q- Q7 (Q- QH) ) 輸出 0b01010101,那麼首先就是將第 14 接腳 (DS, SER) 設為高電位 (High, 1),然後將第 11 接腳 (SH_CP, SRCLK) 做高、低電位的切換 (也就是先 high 再 low),形成一個脈衝訊號 (因為第 11 接腳是上緣觸發) 將資料由移位暫存器(C1)移動到下一個移位暫存器(C2)與存儲暫存器(C3-1
);接著將第 14 接腳設為低電位 (0, low),再將第 11 接腳做 high -> low 變化將資料繼續往下移與旁邊移動....依此類推 (由 b0 到 b7),直到 8 個二進制資料都輸入完成,最後將第 12 接腳 (ST_CP, RCLK) 做 High 再 Low 的變化,這時資料就會一次由 ( Q0 - Q7 (Q- QH) ) 並行輸出,輸出 0b01010101,這就是 SN74HC595 串列輸入並行輸出的控制方式。

如果是使用多個 595 IC 串接在一起,那個上一個 595 IC 的第九支接腳,就是接到下一個 595 IC 的第 14 之接腳做為資料輸入。並行輸出就是等到全部的串列資料都輸入完成後再一起將第 12 支接腳做 High 再 Low 的變化就可以一起做並行輸出。

要一次清除所有暫存器裡面的資料 (清零),只要把第 10 支接腳設為低電位,然後再將第 12 支接腳做 High 再 Low 的變化,就可以將所有串接的 595 IC 清零了。

第 13 支接腳的功能可以做為第三態的輸出,也就是除了 High, Low 之外的第三種輸出狀態,類似於將 pin 腳整個拔起,不與任何東西做連接的狀態。


備註:
三態輸出常用在 LED 的控制上,但控制線路需要單一支接腳做三態輸出並與其他接腳作配合才行 (595 一次輸出八個位元,使用 Charlieplexing 技術控制 LED 有難度)。可以參考 WiKiPedia 上關於 Charlieplexing 技術的說明;另外這裡有一篇中文的說明:基於 Charlieplexing 算法的 LED 矩陣控制設計


74HC595 晶片模擬網站:

這裡另外提供了一個使用 595 控制一個七段顯示器的模擬網站,經由自己動手觸發 595 各接腳的時脈輸入,控制七段顯示器的數字顯示,這對於了解 595 晶片的操作與程式設計非常有用: 595 八位元移位暫存器模擬網站

照著上面的說明或是依照模擬網站中的英文操作流程,使用左邊的按鈕控制輸出七段顯示器的數字。
當可正確的顯示出數字,那差不多就了解這顆晶片要怎麼用了。

source: conductiveresistance.com, 595 Shift Register Simulator

在進入範例程式之前,要先了解 ATtiny45 沒有像 ATmega168 擁有 Master/Slave SPI Serial Interface ,只有 Universal Serial Interface (USI),但是使用 USI 的三線模式 (three-wire mode)可以做到符合 SPI 模式 0 和 1,除了缺少 SS (Slave Select) 的接腳功能,不過這可已利用軟體做到。

在 USI 三線模式下會使用到的接腳名稱為 DO、DI、USCK




AVR 程式範例程式說明:

材料列表(下面兩個例子):
  • Attiny45 (Ateml 出的 8-bit AVR 微處理器),PDF, x 1

  • SN74HC595 (三態輸出八位元移位暫存器 IC),PDF, x 2

  • 電阻 (1/4W, 5%), 10k x 1, 330 x 16

  • LED (Vf=3.3V),限流在 5mA 左右

  • 七段顯示器 (共陰), x 2

595 是用來做 IO 擴充用的,但使用的方式就是把它當作 SPI Slave 來用。

595 接線上,由於我們不使用三態輸出因此將 OE 接地、 SRCLR 接 Vcc 表示不會手動清除移位暫存器、控制並行輸出的接腳 RCLK 可依需要接到微處理器任一根沒被使用到的接腳。電路圖中畫虛線是表示這條線可接可不接。

下面各有兩個例子,一個是使用 595 控制 Attiny45 的 Timer 依設定的時間間隔點亮 LED 燈,另一個是使用兩顆 595 控制分別兩顆七段顯示器,  從 00 顯示到 99,每一個都有相對應的 proteus 模擬軟體檔案,有需要的,連結在這一節最後面。


595 驅動 8 顆 LED



串接兩顆 595 控制七段顯示器




單晶片範例程式、接線電路圖、Proteus 模擬線路圖:

AVR 程式、EagleCAD 電路圖和 Proteus 電路模擬圖面都放在 Google 雲端硬碟上。點擊下面連結前往下載網頁,再點擊左上角的 "在雲端硬碟中開啟" 進入你自己的雲端硬碟畫面,接下來只要點擊 SN74HC595_IOExpander_.rar 檔案下載就可以了,其餘的目錄是壓縮檔裡的東西

壓縮檔:下載

如果要自行編譯或修改內容,使用的版本要比後面列出來的一樣或是更高:
編譯程式使用 AVR Studio 4、Proteus 7.9 、EagleCAD 6.2.0



Raspberry Pi 使用 595 說明:

之前部落格中("Raspberry Pi - BCM2835 C 語言函式庫")曾經介紹過一個在 Raspberry Pi 中使用的 Broadcom BCM2835 C 語言函式庫 (它與 C++ 也是相容的),當時沒真正使用過,剛好可以用來講解接下來要說的 595 晶片驅動的方法與程式寫作。

bcm2835 C library 網址:http://www.open.com.au/mikem/bcm2835/

我希望給各位沒時間研究或是剛入門的人,可以花較少的時間直接使用這個函式庫,利用 C 語言來開發一些需要執行速度比較快的應用。

下面使用一個驅動 8 個 LED 的簡單例子,說明如何使用bcm2835 C語言函式庫控制 GPIO 與控制 595 串入並出 IO 擴充晶片,最後使用 Gcc 編譯後執行程式。


材料列表(下面兩個例子):
  • Raspberry Pi (賣場網址)

  • SN74HC595 - 三態輸出八位元移位暫存器 IC (賣場網址)

  • 電阻 (1/4W, 5%), 330 x 8 或  330 排阻 (9-pin)

  • 普通 LED,黃、綠、藍、紅 ,限流之後總合不要超過晶片的最大電流要求

  • 特製 GPIO 彩色排線 (賣場網址)

  • 麵包版連接測試跳線 (賣場網址)


首先程式架構如下,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <bcm2835.h>
 
int main(int argc, char **argv)
{  
  if(!bcm2835_init())
    return 1;

  while(1)
  {
    ;
  }

  return 0;
}

說明:

Line 1:引入最重要的標頭檔 - bcm2835.h

Line 5-6:只有 bcm2835_init() 函式回傳為 1 才可以正常使用 bcm2835 C 函式庫,沒回傳成功就不能呼叫 bcm2835函式庫裡面的程式,不然使用上會產生問題。

Line 8-12:無窮迴圈,如果程式碼要一直執行的話就插入程式碼在其中,不然可以刪除這幾行。


編譯成執行檔:

將上面的程式碼存檔成 "74hc595_8led.c",或是直接在樹莓派下載上下載程式碼。在終端機提示字元下輸入下面指令:


cd
mkdir SN74595
cd SN74595
sudo wget -O - http://goo.gl/GDkfc | tar xvf -

目錄下就會產生一個原始檔案,

-rwxr-xr-x 1 pi pi 1914  1月  8 14:15 74hc595_8led.c


在命令提示字元下輸入下面指令,確認可以編譯成功,也不要出現任何錯誤就可以執行測試了

sudo gcc 74hc595_8led.c -o 74hc595_8led -l rt -l bcm2835

說明:
8_LED_Flashing.c 是要編譯的程式名稱;-o 後面接的是輸出的執行檔名稱;-l 後面接的是指定的連結函式庫


如果出現錯誤訊息是沒有 bcm2835 函式庫,輸入下面指令下載並安裝 bcm2835 函式庫


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
pi@raspberrypi ~ $ cd
pi@raspberrypi ~ $ wget http://www.open.com.au/mikem/bcm2835/bcm2835-1.20.tar.gz
...
pi@raspberrypi ~ $ tar zxvf bcm2835-1.20.tar.gz
...
pi@raspberrypi ~ $ cd bcm2835-1.20/
pi@raspberrypi ~/bcm2835-1.20 $ ./configure
...
pi@raspberrypi ~/bcm2835-1.20 $ make
...
pi@raspberrypi ~/bcm2835-1.20 $ make check
...
pi@raspberrypi ~/bcm2835-1.20 $ sudo make install
...

說明:

Line 1:單獨輸入 cd 表示回到 Home 目錄下,/home/pi

Line 2:下載 bcm2835-1.20.tar.gz,可以上 C library for Broadcom BCM 2835 as used in Raspberry Pi/ 查看並下載最新版本

Line 4:解壓縮檔案;指令完成後會在目錄下產生 bcm2835-1.20

Line 6:進入 bcm2835-1.20 目錄下

Line 7:執行 configure 主要是確認系統中的函式庫是否符合此軟體需求,若找不到貨式版本太低時,就會產生錯誤訊息並結束程式,這時就必須要上網下載相關的的函式庫下來安裝,再重新執行 configure 確認一次,確認通過後,configure 就會自動產生 Makefile,才能做接下來的程式編譯

Line 9:進行程式編譯

Line 11:安裝之前對環境以及來源程式碼等進行檢測以確保編譯安裝正確

Line 13:進行 bcm2835 函式庫安裝


完成上面步驟後,相信再次編譯就不會再有問題了!!


接線:

Raspberry Pi (Model B)所使用腳位為 GPIO #25, #24, #23,分別接到 595 晶片的 Pin 11, 12, 和 14,對照下面的線路圖與表格中的說明接好如下的測試線路 (紅色虛線表示 3V3 與 5V 兩者選其一,都可以接在 595 晶片上)。

Raspberry Pi 控制 595 晶片驅動 8 個 LED 接線圖


RPi GPIO # (P1) SN74595N 說 明
3V3 (1) Vcc (16), SRCLR (10) 595 Vcc 和 SRCLR 接 RPi GPIO P1 3.3V 5V 電源
GND (25) GND (8), OE (13) 595 GND 和 OE 接 RPi GPIO P1 GND
#25 (22) SRCLK (11) RPi GPIO #25 (P1 Pin-22) 接 595 Pin-11
#24 (18) RCLK (12) RPi GPIO #24 (P1 Pin-18) 接 595 Pin-12
#23 (16) SER (14) RPi GPIO #23 (P1 Pin-16) 接 595 Pin-14

QA (15)
QB ~ QH (1 ~ 7)
MSB (高位元) . . . .  LSB (低位元)
  QH  . . . . . . . . . . . . . QA


實際接線在麵包板上的情形
樹莓派與 595 晶片在麵包板上的接線圖


在 #incldue 程式碼下面輸入下面接腳的定義

1
2
3
4
// 接腳定義
#define SER                      23  // 資料輸入
#define SRCLK                    25  // 將資料推入移位暫存器
#define RCLK                     24  // 8 位元並行輸出


依照上面操作流程的說明,可以知道每次 (從SER) 輸入一個 bit 後,必須 (在 SRCLK) 給一個上緣 (也就是 _||_) 脈衝訊號將資料推進去移位暫存器。8 個 bit 全部輸入後,最後再 (在 RCLK) 給一個上緣脈衝訊號將資料移入儲存暫存器,由於我們將 OE 接地,所以剛剛串列輸入的資料就會立刻同時由儲存暫存器輸出到接腳 QA ~ QH

將下面的程式碼輸入到 "while(1) {" 描述之後," }" 之前

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 /*- Start -*/
 // Set the pin to be an output
 bcm2835_gpio_fsel(SER, BCM2835_GPIO_FSEL_OUTP);
 bcm2835_gpio_fsel(SRCLK, BCM2835_GPIO_FSEL_OUTP);
 bcm2835_gpio_fsel(RCLK, BCM2835_GPIO_FSEL_OUTP); 
 
 // 初始輸出狀態
 bcm2835_gpio_write(SER, LOW);
 bcm2835_gpio_write(SRCLK, LOW);
 bcm2835_gpio_write(RCLK, LOW);
 
 bcm2835_gpio_write(SER,LOW);
 //bcm2835_delayMicroseconds(1);
 
 bcm2835_gpio_write(SRCLK, HIGH);
 //bcm2835_delayMicroseconds(1);
 bcm2835_gpio_write(SRCLK, LOW);
 //bcm2835_delayMicroseconds(1);
  
 bcm2835_gpio_write(RCLK, HIGH);
 //bcm2835_delayMicroseconds(1);
 bcm2835_gpio_write(RCLK, LOW);
 //bcm2835_delayMicroseconds(1);
 /*- End -*/

說明:

Line 3 ~ 5:將定義的 RPi 接腳設定為輸出

Line 8 ~ 10:將定義的 RPi 接腳全設定為低電位。因為 595 晶片 SRCLK 和 RCLK 控制接腳為上緣觸發,必須由低到高電位做訊號變換 (上緣觸發) 才會有作用

Line 12 ~ 13:設定資料接腳為高電位

Line 15 ~ 18:SRCLK 接腳由低到高電位做變換,將剛剛 SER 的高電位訊號推進到移位暫存器 QH

Line 20 ~ 23:RCLK 接腳由低到高電位做變換,將移位暫存器裡全部的資料,全部輸出到接腳 QA ~ QH

ps. 可以看到在每個空置接腳訊號作變得程式碼下面都有一行延遲程是被忽略掉,這是因為要測試在每個控制訊號輸入到 595 時,延遲的間隔最低能到多少,晶片還能正常接收到由 RPi 輸出的訊號。經過測試,可以將其省略,若是您在使用上有位元移失的情形出現,先將上面忽略的程式碼前面 // 取消掉並加上延遲時間再做測試,會是一個好的選擇。


來看看剛剛程式執行後的結果!在命令提示字元下輸入下面指令,進行編譯。編譯成功後,直接執行,這時您將會看到連接到 595 晶片 QA 接腳的 LED 燈亮起。

sudo gcc 74hc595_8led.c -o 74hc595_8led -l rt -l bcm2835
sduo ./74hc595_8led

繼續再執行指令 sudo ./74hc595_8led,亮起來的就變成是 QA 和 QB 兩顆 LED 亮起;一直執行下去,到第八次就會點亮所有的 LED。

上面就是輸入一個位元 (bit) 的資料到 595 並做輸出的程式。若是一次要輸入 8 個位元資料再輸出,就要重覆 Line 12 ~ 18 行程式 8 次,方法是將位元組 (byte) 拆解,由高位元 (MSB) 到低位元 (LSB) 依次由 SER 接腳輸出,將資料輸入到移位暫存器裡,最後再執行 Line 20 ~ 23 作並行輸出。如果串接多個 595 晶片,就是一直重複的執行 Line 12 ~ 18,最後再執行 Line 20 ~ 23 作並行輸出。


依照上面的描述結合成一個 SIPO 程式,負責將位元組的資料餵給 595 晶片。而上緣觸發訊號則呼叫 pulse 程式完成。再結合 8 顆 LED 做往復由左到右再由右到左的單顆 LED 亮滅為一個例子,完成一個撰寫在樹莓派控制 595 晶片的 74hc595_8led.c 程式:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <stdio.h>  // printf
#include <bcm2835.h>

// 接腳定義
#define SER                      23  // 資料輸入
#define SRCLK                    25  // 將資料推入移位暫存器
#define RCLK                     24  // 8 位元並行輸出

void SIPO(unsigned char byte);
void pulse(uint8_t pin);

int main(int argc, char **argv)
{
 int i;
 unsigned char LED[8]={0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
  
 if(!bcm2835_init())
  return 1;
  
 /*- Start -*/
 // Set the pin to be an output
 bcm2835_gpio_fsel(SER, BCM2835_GPIO_FSEL_OUTP);
 bcm2835_gpio_fsel(SRCLK, BCM2835_GPIO_FSEL_OUTP);
 bcm2835_gpio_fsel(RCLK, BCM2835_GPIO_FSEL_OUTP); 
 
 // 初始輸出狀態
 bcm2835_gpio_write(SER, LOW);
 bcm2835_gpio_write(SRCLK, LOW);
 bcm2835_gpio_write(RCLK, LOW);
  
 while(1)
 {  
  for(i = 0; i < 8; i++)
  {
   SIPO(LED[i]);
   pulse(RCLK);
   bcm2835_delay(50);
   printf(" i = %d", i);
  }
  printf("\n");
  bcm2835_delay(500); // 500 ms
  
  for(i = 7; i >= 0; i--)
  {
   SIPO(LED[i]);
   pulse(RCLK);
   bcm2835_delay(50);
   printf(" i = %d", i);
  }
  printf("\n\n");
  bcm2835_delay(500); // 500 ms
 }
  
 /*- End -*/

 return 0;
}

void SIPO(unsigned char byte)
{
    int i;
    
    for(i = 0; i < 8; i++)
    {
     // DEBUG
        //printf("((byte & (0x01 << %d)) > 1) = ((%#x & %#x) > 1) = %d \n", i, byte, (0x01 << i), ((byte & (0x01 << i)) > 1));
        
        bcm2835_gpio_write(SER,((byte & (0x80 >> i)) > 0));
        //bcm2835_delayMicroseconds(1);
        
     // 給個脈衝訊號,將資料推進移位暫存器
     pulse(SRCLK);     
    }
}

// 產生一個脈衝訊號
void pulse(uint8_t pin) // uint8_t = unsigned int
{
 bcm2835_gpio_write(pin, HIGH);
    //bcm2835_delayMicroseconds(1);
    bcm2835_gpio_write(pin, LOW);
    //bcm2835_delayMicroseconds(1);
}


同樣的,依照上面編譯的指令將上面程式碼編譯成執行檔,然後接上線路就可以測試了,離開程式按 "CTRL+C"。成功之後,可以將 595 的電源換成 5V 試試,都是可以正常動作的。

編譯好的執行檔與原始程式碼請上賣場雲端硬碟資料夾下載,如果您有 Google 帳號,就按下左上角 "在雲端硬碟中開啟" 按鈕,直接將整個資料夾打包壓縮下載

程式檔下載


上面就是如何在 Raspberry Pi (2012-10-28-wheezy-raspbian) 作業系統環境下,如何使用 bcm2835 函式庫控制 GPIO 的程示範例,希望這簡單的程式能夠帶給想要使用 C 語言轉寫程式的人有所幫助,這只是入門!剩下的就要靠自己再深入去研究了!


補充資料:

至於使用 python 撰寫控制 595 晶片的程式,可以參考下面這個網站中的教學,作者是使用 spi 的方式做控制,如果對於使用 SPI 方式有興趣的可以參考一下,該網頁最下方有原始碼可以下載

All the outputs you could ever want !


source:  http://rasathus.blogspot.co.uk




規格表說明:

SN74HC595 輸入電壓建議值在 2V 至 6V ,source 和 sink 的電流最大可達 35mA (但總和不要超過 70mA),如果要驅動外部裝置 (像是 LED),要注意不要超過 IC 的極限值,也要注意到連接到 SN74HC595N 的 Vcc-Gnd 之間的電源供應是否可提供足夠的電流,若不行就要外接。


下面是 SN74HC595N 的規格表。





SN74595N IC 包含一個8位元串行輸入,並行輸出移位暫存器,並將資料餵給下一級 8位元 D 型存儲暫存器,而存儲寄存器是並行三態輸出 (Hign, Low, High-Impedance)。

移位與存儲暫存器由個別的時脈輸入作控制;移位暫存器有直接清除輸入的 SRCLR 接腳、以及用在將資料串列輸入的 SER 接腳、和連接做為下一級晶片輸入的  QH' 接腳。當 OE 接腳為 high 時,輸出是為高阻抗的狀態,也就是接腳不接 Vcc 也不接 Gnd ,是處在浮接的狀態。

移位暫存器時脈(SRCLK)和存儲暫存器時脈(RCLK)都是正緣觸發,如果兩個接腳的輸入時脈接在一起,則移位暫存器就會超前存儲暫存器一個時脈訊號。


SN74HC595N特點:
  • 8位串列輸入,並行輸出移位

  • 2 V 至 6 V 工作電壓範圍

  • 大電流三態輸出可以驅動多達15輸入通道負載

  • 低功耗:80μA(最大值)ICC

  • TPD= 13 ns(典型值)

  • 低輸入電流:1μA(最大值)

  • 可直接清除移位暫存器的內容值



接腳圖:

source: TI.com, SN74HC595


真值表:



邏輯圖:

source: TI.com, SN74HC595

6 則留言:

  1. 回覆
    1. 這裡只是要說明 595 可使用 AVR 的 SPI port (MOSI, MISO, SCK)作控制,而 AVR 的三線模式(three-wire mode)是與 SPI 相容,595不是純 SPI 介面但可用 SPI 協議方式作控制。

      刪除
    2. 這裡有一篇文章,我看了一下,跟您作分享

      SPI-Configuration Example: 74HC595
      http://wiki.netduino.com/SPI-Configuration-Example-74HC595.ashx?Code=1

      刪除
    3. 了解。SRCLK, SER, RCLK 分別相當於 SPI 的 SCK, MOSI, nSS。nSS 作為 RCLK,結束的 rising 剛好將資料載入儲存暫存器。
      謝謝。

      刪除
  2. 74595 八位元的三態輸出是一起控制的,似乎無法用在 Charlieplexing 算法的 LED 矩陣控制,例如用八位元來控制 56 顆 LED。

    回覆刪除
    回覆
    1. 您說的對!
      595 RCLK 觸發時八位元是一起輸出的,而使用 Charlieplexing 的技術需要單一接腳可做三態。
      我將內容修改一下,避免誤會,謝謝您的指教!

      刪除

留言屬名為"Unknown"或"不明"的用戶,大多這樣的留言都會直接被刪除掉,不會得到任何回覆!

發問問題,請描述清楚你(妳)的問題,別人回答前不會想去 "猜" 問題是什麼?

不知道怎麼發問,請看 [公告] 部落格提問須知 - 如何問問題 !