شیفت رجیستر چیست و چگونه کار میکند؟

تا به حال پیش آمده بخواهید تعداد زیادی LED را کنترل کنید؟ یا به ورودی/خروجیهای بیشتری برای پروژه خود نیاز داشته باشید؟ این آموزش اصول اولیه فناوری ای را پوشش میدهد که به شما اجازه میدهد دقیقاً همین کار را انجام دهید. نام آن شیفت رجیستر (Shift Register) است. اما دقیقاً چیست؟ چرا مفید است؟ و چطور باید از آن استفاده کرد؟ در این آموزش به تمام این پرسشها پاسخ خواهیم داد.
شیفت رجیستر چیست؟
شیفت رجیستر دستگاهی است که به شما اجازه میدهد ورودیها یا خروجیهای اضافی را به یک میکروکنترلر اضافه کنید.

مدل SN74HC595 یکی از معروفترین شیفت رجیسترهای 8 بیتی است که تنها با قیمت حدود 1.05 دلار در دسترس است.
عملکرد آن بر اساس تبدیل دادهها بین فرمتهای سریال و موازی است. میکروکنترلر با شیفت رجیستر از طریق دادههای سریال ارتباط برقرار میکند، در حالی که خود شیفت رجیستر دادهها را بهصورت موازی (چند پایهای) جمعآوری یا ارسال میکند.

اگر با دکمهها (Push Button) زیاد کار میکنید، یک شیفت رجیستر میتواند بهخوبی مدیریت ورودیهای متعدد را ساده کند.
انواع SIPO و PISO در شیفت رجیسترها
شیفت رجیسترها بهطور کلی در دو نوع اصلی تولید میشوند:
- SIPO (Serial-In Parallel-Out) — ورودی سریال و خروجی موازی
- PISO (Parallel-In Serial-Out) — ورودی موازی و خروجی سریال
شرکت SparkFun هر دو نوع را عرضه میکند. مدل 74HC595 نوع SIPO است و برای کنترل تعداد زیادی خروجی مانند LEDها بسیار کاربردی است. در مقابل، مدل 74HC165 از نوع PISO بوده و برای جمعآوری ورودیهای زیاد مانند دکمهها (buttons) مناسب است.
SparkFun همچنین نسخههای Breakout Board از هر دو تراشه را ارائه داده که استفاده از آنها را بسیار آسانتر میکند.
اگر به بیش از 8 ورودی یا خروجی اضافی نیاز دارید، میتوانید چندین شیفت رجیستر را بهصورت زنجیرهای به هم متصل کنید؛ یعنی خروجی رجیستر اول را به ورودی رجیستر بعدی متصل نمایید.
به این ترتیب، تنها با چند پایه از میکروکنترلر میتوانید دهها ورودی و خروجی جدید ایجاد کنید.

چرا باید بیت ها را شیفت داد؟
شیفت رجیسترها معمولاً برای صرفهجویی در پایههای میکروکنترلر استفاده میشوند. هر میکروکنترلر تعداد محدودی پایهی ورودی/خروجی عمومی (GPIO) دارد.
فرض کنید در یک پروژه باید 16 عدد LED را کنترل کنید. در حالت عادی، این کار به 16 پایهی خروجی نیاز دارد. اما اگر میکروکنترلر شما چنین تعداد پینی نداشته باشد، اینجاست که شیفت رجیستر وارد عمل میشود.
با استفاده از دو شیفت رجیستر متصل بهصورت زنجیرهای (cascade) میتوان همین 16 LED را تنها با 4 پایه از میکروکنترلر کنترل کرد. این تفاوت بسیار چشمگیری است، و هر چه شیفت رجیسترهای بیشتری در زنجیره قرار دهید، میتوانید پایههای بیشتری را ذخیره کنید.

یک نمونهی معروف از استفادهی واقعی شیفت رجیستر برای جمعآوری ورودیها در کنترلر اصلی کنسول نینتندو (NES) دیده میشود. میکروکنترلر اصلی NES از شیفت رجیستر برای دریافت وضعیت دکمههای کنترلر استفاده میکرد.
نمونه ی عملی با برد آردوینو
در این مثال از برد Breakout تراشهی 74HC165 و یک Arduino Uno استفاده میکنیم تا نحوهی انجام عملیات Parallel-In Serial-Out (PISO) را نشان دهیم.

میتوانید این مقاله را هم ببینید: استفاده از شیفت رجیستر 74HC595 با آردوینو
هر تراشهی شیفت رجیستر 8 بیتی به 4 خط ارتباطی از میکروکنترلر نیاز دارد:
- Clock (CLK): برای زمانبندی انتقال دادهها
- Clock Enable (CE): برای فعالکردن سیگنال ساعت
- Shift/Load (SH/LD): برای بارگذاری یا شیفت دادهها
- Serial Data (SER_OUT): برای ارسال دادههای سریال
اتصالات سختافزاری (Hardware Hookup):

- پایهی CLK را به پین 12 آردوینو وصل کنید.
- پایهی CE (Clock Enable) را به پین 9 وصل کنید.
سیگنال ساعت مشخص میکند با چه فرکانسی بیتها شیفت شوند، در حالی که Clock Enable تعیین میکند آیا این سیگنال اجازهی عبور دارد یا نه. - پایهی SH/LD (Shift/Load) را به پین 8 وصل کنید.
وقتی این پایه به سطح Low میرود، شیفت رجیستر وضعیت فعلی 8 پایهی ورودی (A تا H) را میخواند.
این پایهها میتوانند به دکمهها، کلیدها یا مدارهای دیجیتال متصل شوند. برای تست، میتوانید آنها را مستقیم به VCC یا GND متصل کنید تا از صحت عملکرد اطمینان یابید.
در این مثال، یکی از پایهها را به دکمهای با مقاومت Pull-Up وصل میکنیم و بقیه را به منبع تغذیه یا زمین. - پایهی SER_OUT (Serial Out) را به پین 11 آردوینو وصل کنید. این پایه دادههای سریال را از شیفت رجیستر ارسال میکند. همچنین پایهی SER_IN (Serial In) را به زمین (GND) متصل کنید.
- اگر چند شیفت رجیستر را زنجیرهای متصل کرده باشید، خروجی سریال یکی باید به ورودی سریال بعدی وصل شود. در این حالت، ورودی سریال رجیستر اول به زمین وصل میشود و خروجی سریال آخر به میکروکنترلر برمیگردد.
در نهایت، تغذیه (2 تا 6 ولت) و زمین (GND) را نیز متصل کنید.
با انجام این اتصالات، سختافزار آماده است و میتوانیم سراغ برنامهنویسی (Firmware) برویم.
میانافزار (Firmware)
در این بخش توضیح کوتاهی دربارهی عملکرد کد ارائه شده است. ابتدا، تمام پایههایی که به شیفت رجیستر متصل کردهایم (بهجز پایهای که دادهی سریال از آن دریافت میشود) بهعنوان خروجی (OUTPUT) مقداردهی اولیه میشوند.
سپس پایههای Clock و Shift مطابق با جدول زمانبندی در دیتاشیت روی حالت اولیهی HIGH تنظیم میشوند.
برای خواندن وضعیت پایههای A تا H، باید به شیفت رجیستر فرمان دهیم تا وضعیت فعلی آنها را ثبت کند. این کار با پایین آوردن موقت پایهی Load (حدود ۵ میکروثانیه) انجام میشود. پس از بارگذاری دادهها، پایهها دوباره به حالت اولیه برمیگردند و با استفاده از تابع shiftIn در آردوینو، مقدار تمام ۸ پایهی A تا H بهصورت یک بایت در متغیر incoming ذخیره میشود.
مقادیر خواندهشده در Serial Monitor چاپ میشوند، سپس برنامه کمی صبر کرده و دوباره همین روند را تکرار میکند.
اگر اتصالات سختافزاری طبق دستورالعملهای بخش قبل انجام شده باشند، اجرای این کد بهراحتی عملکرد مدار را نشان خواهد داد.
کد آردوینو:
// HARDWARE CONNECTIONS
// Connect the following pins between your Arduino and the 74HC165 Breakout Board
// Connect pins A-H to 5V or GND or switches or whatever
const int data_pin = 11; // Connect Pin 11 to SER_OUT (serial data out)
const int shld_pin = 8; // Connect Pin 8 to SH/!LD (shift or active low load)
const int clk_pin = 12; // Connect Pin 12 to CLK (the clock that times the shifting)
const int ce_pin = 9; // Connect Pin 9 to !CE (clock enable, active low)
byte incoming; // Variable to store the 8 values loaded from the shift register
// The part that runs once
void setup()
{
// Initialize serial to gain the power to obtain relevant information, 9600 baud
Serial.begin(9600);
// Initialize each digital pin to either output or input
// We are commanding the shift register with each pin with the exception of the serial
// data we get back on the data_pin line.
pinMode(shld_pin, OUTPUT);
pinMode(ce_pin, OUTPUT);
pinMode(clk_pin, OUTPUT);
pinMode(data_pin, INPUT);
// Required initial states of these two pins according to the datasheet timing diagram
digitalWrite(clk_pin, HIGH);
digitalWrite(shld_pin, HIGH);
}
// The part that runs to infinity and beyond
void loop() {
incoming = read_shift_regs(); // Read the shift register, it likes that
// Print out the values being read from the shift register
Serial.println("\nThe incoming values of the shift register are: ");
Serial.print("ABCDEFGH : ");
print_byte(incoming); // Print every 1 and 0 that correlates with A through H
//Serial.println(incoming,BIN); // This way works too but leaves out the leading zeros
delay(2000); // Wait for some arbitrary amount of time
}
// This code is intended to trigger the shift register to grab values from it's A-H inputs
byte read_shift_regs()
{
byte the_shifted = 0; // An 8 bit number to carry each bit value of A-H
// Trigger loading the state of the A-H data lines into the shift register
digitalWrite(shld_pin, LOW);
delayMicroseconds(5); // Requires a delay here according to the datasheet timing diagram
digitalWrite(shld_pin, HIGH);
delayMicroseconds(5);
// Required initial states of these two pins according to the datasheet timing diagram
pinMode(clk_pin, OUTPUT);
pinMode(data_pin, INPUT);
digitalWrite(clk_pin, HIGH);
digitalWrite(ce_pin, LOW); // Enable the clock
// Get the A-H values
the_shifted = shiftIn(data_pin, clk_pin, MSBFIRST);
digitalWrite(ce_pin, HIGH); // Disable the clock
return the_shifted;
}
// A function that prints all the 1's and 0's of a byte, so 8 bits +or- 2
void print_byte(byte val)
{
byte i;
for(byte i=0; i<=7; i++)
{
Serial.print(val >> i & 1, BIN); // Magic bit shift, if you care look up the <<, >>, and & operators
}
Serial.print("\n"); // Go to the next line, do not collect $200
}
نمونه خروجی در Serial Monitor:
The incoming values of the shift register are: ABCDEFGH : 11110000
حالا میتوانید هر پایهی ورودی را به دکمه متصل کنید یا یک شیفت رجیستر دیگر به مدار اضافه کنید.
اگر چند رجیستر را زنجیرهای متصل کردید، باید در کد تغییر کوچکی بدهید: دادهها را یک بار بارگذاری کنید، سپس برای هر رجیستر موجود، یک بار تابع shiftIn() را اجرا کنید و پس از پایان خواندن، دوباره عملیات بارگذاری را انجام دهید.
اکنون میدانید وقتی در پروژهای با کمبود پایههای ورودی/خروجی روبهرو شدید، از چه سختافزاری باید استفاده کنید.
اگر علاقهمندید بدانید این قطعات دقیقاً چگونه کار میکنند، مطالعهی منطق دیجیتال (Digital Logic) را پیشنهاد میکنم. در نهایت با مفهومی به نام Flip-Flop آشنا میشوید که اساس عملکرد شیفت رجیسترهاست. برای درک عمیقتر نیز، نمودارهای زمانی (Timing Diagram) در دیتاشیتها را بررسی کنید تا متوجه شوید در هر لحظه چه اتفاقی درون رجیستر رخ میدهد.









