กลับไปหน้ารวมไฟล์
shift-registers-tutorial-2-relative-addressing-2990f4.md

ถ้าน้องบังเอิญหลุดมาอ่านบทความนี้โดยไม่ได้เริ่มจากจุดเริ่มต้นของซีรี่ย์ มันอาจจะดูงงๆ หน่อยนะ เพราะบทความนี้เป็นหนึ่งในหกบทเรียนที่ช่วยให้เข้าใจการใช้ไลบรารี ez_SIPO8_lib ในการจัดการและควบคุมไอซีแบบ Serial-in/Parallel-out (SIPO) หรือที่เรียกกันว่า shift registers เช่น ชิป 74HC595 นั่นเอง

ถ้าอยากเริ่มอ่านจากบทความแรกของซีรี่ย์ ก็กดลิงก์นี้ไปเลย (จุดเริ่มต้นของบทเรียน) ไม่งั้นก็อ่านต่อเลยจ้า...

บทเรียนที่สองเพื่อเสริมความเข้าใจและการใช้งานไลบรารี ez Serial-in/Parallel-out IC (ez_SIPO8_lib) - เรื่อง Relative Addressing ถ้าอยากกลับไปอ่านบทความแรกของซีรี่ย์ ก็กดลิงก์นี้: จุดเริ่มต้นของบทเรียน.
คู่มือผู้ใช้, เอกสารสรุป และบทความต้นฉบับของ ez_SIPO8_lib สามารถเข้าถึงและดาวน์โหลดได้ตามลิงก์ด้านล่างนี้:
- ez_SIPO8_lib คู่มือผู้ใช้
- ez_SIPO8_lib เอกสารสรุป
- อ่านบทความเต็มของ ez_SIPO8_lib

มุมมองของโปรเจกต์

Shift Registers - บทเรียนที่ 2, Relative Addressing เป็นบทเรียนที่เข้าใจง่ายและยืดหยุ่นเกี่ยวกับการกำหนดที่อยู่แบบสัมพัทธ์ (Relative Addressing) ของ shift registers โดยใช้ Shift Register 74HC595 และ LED 8 ดวง น้องจะได้สร้างโปรเจกต์อิเล็กทรอนิกส์ที่ขั้นสูงขึ้นและเรียนรู้เกี่ยวกับลอจิกดิจิทัลพื้นฐาน

แนะนำบทเรียน

ในบทเรียนที่สองนี้ เราจะต่อยอดจากประสบการณ์ในบทเรียนที่ 1 และมาดูเรื่องการกำหนดที่อยู่แบบสัมพัทธ์ (Relative Addressing) ของพูลพินเอาต์พุตเสมือนของ SIPO การกำหนดที่อยู่รูปแบบนี้ใช้เพื่ออ้างอิงถึงพินเอาต์พุตของ SIPO ที่เกี่ยวข้องกับ 'banks' — โดยที่ bank คือชุดของไอซี SIPO ทั้งแบบจริงและแบบเสมือน ซึ่งอาจหมายถึง SIPO IC ตัวเดียว หรือ หลายตัว (เยอะมาก!) ที่ต่อกันเป็นแบบ cascade

เป้าหมาย

คราวนี้เราจะมาสร้างโลกเสมือนของ SIPO กัน เพื่อเอาไว้ทดลองหลักการพื้นฐานต่อๆ ไป โดยเฉพาะเราจะได้เรียนรู้:

1. วิธีการต่อวงจรและเชื่อมต่อ IC 74HC595 (SIPO) ตัวเดียวเข้ากับไมโครคอนโทรลเลอร์ (ถ้ายังไม่ได้ทำใน Tutorial 1 นะ)

2. โครงสร้างของ Arduino sketch ที่ตั้งค่าไว้สำหรับขับ IC SIPO และ LED ที่ต่ออยู่จริงๆ โดยเฉพาะ:

a. การสร้าง "ธนาคาร" SIPO เสมือน
b. การใช้ relative addressing เพื่อเข้าถึง/อัพเดตธนาคาร SIPO เสมือนที่สร้างขึ้น

3. ได้เห็นผลลัพธ์จาก sketch – แบบแผนไฟ LED ที่วิ่ง และข้อมูล SIPO บน Serial Monitor

ถ้าน้องทำ Tutorial 1 มาแล้วและยังตั้งชิ้นส่วนไว้เหมือนเดิม ก็ข้ามไปที่ส่วน The Code/Sketch ได้เลย ไม่งั้นก็อ่านต่อด้านล่างนี้จ้า

การลงมือทำจริง (Technical Implementation)

ระบบนี้ใช้ 74HC595 Shift Register ในการควบคุม LED 8 ดวง โดยใช้แค่พินดิจิทัล 3 พินบน Arduino เท่านั้น Tutorial นี้เน้นเรื่อง relative addressing ซึ่งจะทำให้เราสามารถเซ็ตบิตเฉพาะเจาะจงบน shift register ได้ตามตำแหน่งสัมพัทธ์ของมัน

โครงสร้างฮาร์ดแวร์

  • Arduino Uno: ตัวควบคุมหลักสำหรับ shift register และลอจิกทั้งหมด
  • 74HC595 Shift Register: ชิป 8-bit serial-in, parallel-out ตัวนี้คือหัวใจของการควบคุม LED
  • LED 8 ดวง: ให้ผลตอบรับแบบเห็นๆ กับตา เกี่ยวกับสถานะของ shift register
  • ตัวต้านทาน 220 โอห์ม: ใช้เพื่อป้องกัน LED และพินเอาต์พุตของ Arduino
  • สายจัมเปอร์: ใช้เชื่อมต่อชิ้นส่วนทั้งหมดเข้าด้วยกัน

รายการของที่ต้องใช้

รวบรวมชิ้นส่วนต่อไปนี้ให้ครบ:

  • 1 x Arduino UNO - ดีไซน์นี้ใช้ Arduino UNO แต่ไมโครคอนโทรลเลอร์ตัวไหนก็ได้ (Arduino หรือของโคลน) ตราบใดที่มันรองรับความต้องการของพินสำหรับขับอินเตอร์เฟซ SIPO และเรื่องไฟเลี้ยง
  • 1 x Breadboard - เล็กหรือใหญ่ อันไหนที่หยิบได้ใกล้มือก็ใช้เลย
  • 1 x IC 74HC595 - IC SIPO 8bit หรือของโคลนอื่นๆ ที่ "plug-compatible" จริงๆ
  • 8 x LED - อะไรก็ได้ที่มีอยู่รอบตัว
  • 8 x ตัวต้านทาน 220 โอห์ม - หนึ่งตัวต่อ LED หนึ่งดวง ใช้ 220 โอห์มนะ ห้ามใช้ 180 โอห์มตามที่แผนภาพบางอันแนะนำ เดี๋ยวไฟไหม้!
  • สายต่อวงจร - สั้น/ยาว หรือสายสำหรับเบรดบอร์ด อันไหนที่เหมาะกับงานก็ใช้เลย

การวางทิศทางและขาของ 74HC595

แล้วด้านไหนเป็นด้านไหนล่ะ? ง่ายๆ เลย ให้สังเกตที่ IC 74HC595 จะมีรอยบาก (notch) อยู่ที่ด้านหนึ่ง ตามรูปด้านบน การนับขาเริ่มจากขาที่ 1 ที่มุมซ้ายบน แล้วไล่ลงมาทางซ้ายมือ จากนั้นวนไปรอบๆ ด้านล่างของ IC แล้วขึ้นมาทางขวามือจนถึงขาที่ 16:

Pin Outs 1 - 74HC595

การตั้งค่าขาเชื่อมต่อระหว่างไมโครคอนโทรลเลอร์กับ SIPO

ทุกๆ แบงค์ของ IC SIPO ที่น้องจะเอามาต่อกับไมโครคอนโทรลเลอร์เนี่ย ต้องใช้การเชื่อมต่อแบบดิจิตอล 3 สายนะฮะ ตารางด้านล่างเป็นตัวอย่างการจับคู่ขา (pin mapping) สำหรับบทเรียนนี้ แต่จริงๆ น้องเลือกใช้ขาไหนของไมโครคอนโทรลเลอร์ก็ได้ตามสะดวกเลย แค่ถ้าเปลี่ยนขา อย่าลืมไปแก้ไขการเรียกฟังก์ชัน create_bank ในสเก็ตช์ด้วยล่ะ ไม่งั้นงานไม่เดินนะตัวนี้!

  • ขา 8 ของ UNO ต่อกับ ขา 14 ของ SIPO - ขาข้อมูล (Data Pin) ของ SIPO
  • ขา 9 ของ UNO ต่อกับ ขา 12 ของ SIPO - ขาล็อคข้อมูล (Latch Pin) ของ SIPO
  • ขา 10 ของ UNO ต่อกับ ขา 11 ของ SIPO - ขานาฬิกา (Clock Pin) ของ SIPO
  • ขา +5v ของ UNO ต่อกับ ขา 10, 16 ของ SIPO - จ่ายไฟเลี้ยงให้ SIPO
  • ขา GND ของ UNO ต่อกับ ขา 8, 13 ของ SIPO - ต่อกราวด์ (0v)

มาลงมือต่อสายกันเถอะ!

ใช้แผนภาพด้านล่างเป็นไกด์ไลน์ ต่อสายให้ครบทุกส่วนให้เรียบร้อย ระวังเรื่องขาเข้า-ขาออกให้ดีนะ ผิดนิดเดียวไฟไม่ติดแน่!

แผนภาพการต่อสายแบบที่ 1 - SIPO IC เดียว ควบคุม 8 เอาต์พุต

สังเกตดีๆ นะ จะเห็นว่ามีขาเดียวของ IC 74HC595 ที่เราไม่ได้ต่ออะไรเลย นั่นคือขาที่ 9 (QH’) ขานี้เอาไว้ใช้เป็นขาส่งข้อมูลแบบอนุกรม (serial output) เพื่อต่อเข้ากับขารับข้อมูล (serial input) ขาที่ 14 (SER) ของ IC SIPO ตัวต่อไป เวลาเราต่อแบบเรียงซ้อน (cascade) นั่นเอง

ตรรกะและกระบวนการส่งสัญญาณ

โค้ด Arduino ใช้ฟังก์ชัน shiftOut() ในการส่งข้อมูลไปยัง shift register

  1. จังหวะนาฬิกา (Clock Sync): Arduino จะเซ็ตขานาฬิกาเป็น HIGH แล้วตามด้วย LOW เพื่อสร้างจังหวะในการโอนถ่ายข้อมูล
  2. การโอนถ่ายข้อมูล (Data Transfer): แต่ละบิตของข้อมูล 8 บิต จะถูกส่งออกไปทางขาข้อมูลแบบทีละบิต (serially)
  3. อัพเดทเอาต์พุต (Latch Activation): ขาล็อคจะถูกปั๊มสัญญาณเป็น HIGH แล้ว LOW เพื่ออัพเดทสถานะของขาเอาต์พุตแบบขนาน (parallel output) ของ shift register

มาดูโค้ด/สเก็ตช์กันดีกว่า

ต่อสายเสร็จแล้ว มาดูสเก็ตช์ที่สาธิตการอ้างอิงแบบสัมพัทธ์ (relative addressing) ของแบงค์/ขากันดีกว่า สเก็ตช์นี้จะสร้างแบงค์ SIPO เดียวที่มีช่วงที่อยู่ขาเอาต์พุต (output pin address) เป็น 0–7 และมี ID ของแบงค์เป็น 0 เพราะมีแค่แบงค์เดียวที่มี SIPO ตัวเดียว ช่วงที่อยู่แบบสัมบูรณ์ (absolute address) ก็เลยตรงกับช่วงที่อยู่ของแบงค์แบบสัมพัทธ์พอดี เรื่องนี้เป็นจริงเสมอสำหรับแบงค์แรกในทุกการตั้งค่า แต่ไม่ต้องกังวลสำหรับสิ่งที่เราจะสาธิตนั่นคือ *การอ้างอิงแบบสัมพัทธ์โดยใช้ฟังก์ชันและเทคนิคการอ้างอิงแบงค์แบบสัมพัทธ์*

สเก็ตช์จะตั้งค่าแบงค์เดียวกันด้วยรูปแบบสัญญาณเอาต์พุต 8 บิตที่แตกต่างกันสองแบบ แล้วอัพเดท (หรือที่เรียกในภาษาของ ez ว่า xfer) ส่งไปยัง IC/LED จริงทีละแบบ โดยมีดีเลย์สั้นๆ คั่นไว้ จากนั้นสเก็ตช์ก็จะวนลูปแบบนี้ไปเรื่อยๆ

สังเกตให้ดีว่าในแต่ละครึ่งของลูปหลัก (main loop) เราใช้วิธีอ้างอิงแบงค์แบบสัมพัทธ์สองวิธีที่แตกต่างกันนะฮะ วิธีแรกคืออ้างอิงทีละบิต (bit) และวิธีที่สองคืออ้างอิงทีละไบต์ (byte) ของ SIPO ในแบงค์ (อัพเดทพอร์ตเอาต์พุต 8 ขาพร้อมกันในทีเดียว)

เปิด Arduino IDE ขึ้นมา สร้างสเก็ตช์ใหม่ แล้วก็โหลดสเก็ตช์จากบทเรียนนี้ไปลองเล่นกันเลย:

//
// Tutorial 2 - use of ez_SPI8 library,
// Relative addressing - 1 x physical SIPO IC
// The sketch demonstrates two ways in which SIPO output pins
// may be updated, individually (set_bank_pin) or in a group
// of 8 pins (set_bank_SIPO), representing a single 8it SIPO
// within a bank.
//
// Ron D Bentley, Stafford, UK
// April 2021
//
// This example and code is in the public domain and
// may be used without restriction and without warranty.
//
#include <ez_SIPO8_lib.h>
#define Max_SIPOs 1 // two virtual SIPOs for this tutorial
#define Max_timers 0 // no timers required
// initiate the class for Max SIPOs/timers required
SIPO8 my_SIPOs(Max_SIPOs, Max_timers);
int bank_id; // used to keep the SIPO bank id
void setup() {
Serial.begin(9600);
bank_id = my_SIPOs.create_bank(8, 10, 9, 1); // absolute pin addresses 0-7, relative addresses 0-7
if (bank_id == create_bank_failure) {
Serial.println(F("\
failed to create bank"));
Serial.flush();
exit(0);
}
// print the bank data for confirmation/inspection
my_SIPOs.print_SIPO_data();
}
void loop() {
// start by setting the only SIPO (0) in the bank to all outputs off/LOW
my_SIPOs.set_bank_SIPO(bank_id, 0, LOW);
my_SIPOs.xfer_bank(bank_id, LSBFIRST); // move virtual pin statuses to real SIPO output pins
do {
//
// setup pattern for first cycle: 0b01010101
// note that set_bank_pin uses relative addressing
my_SIPOs.set_bank_pin(bank_id, 0, HIGH); // least significant bit/pin
my_SIPOs.set_bank_pin(bank_id, 1, LOW);
my_SIPOs.set_bank_pin(bank_id, 2, HIGH);
my_SIPOs.set_bank_pin(bank_id, 3, LOW);
my_SIPOs.set_bank_pin(bank_id, 4, HIGH);
my_SIPOs.set_bank_pin(bank_id, 5, LOW);
my_SIPOs.set_bank_pin(bank_id, 6, HIGH);
my_SIPOs.set_bank_pin(bank_id, 7, LOW); // most significant bit/pin
my_SIPOs.xfer_bank(bank_id, MSBFIRST);
delay(500);
//
// setup reverse pattern using 8bit write function: 0b10101010
// note that set_bank_SIPO uses relative addressing for SIPOs in the bank
my_SIPOs.set_bank_SIPO(bank_id, 0, 0b10101010);
my_SIPOs.xfer_bank(bank_id, MSBFIRST);
delay(500);
} while (true);
}

การทำงานของซอฟต์แวร์

โค้ดนี้ถูกออกแบบมาให้อัพเดต shift register ทุกครั้งที่มีการเปลี่ยนสถานะ และให้มันทำงานสอดคล้องกับสถานะรวมของไฟ LED ทั้งหมด ซึ่งจะทำให้อินเทอร์เฟซผู้ใช้ตอบสนองไวและน่าสนใจมากขึ้น

คอมไพล์สเก็ตช์แล้วอัพโหลดลงบอร์ดได้เลย หลังจากนั้นน้องควรจะเห็นไฟ LED วิ่งสลับไปมาคล้ายกับใน Tutorial 1 โดยมีดีเลย์เล็กน้อยระหว่างแต่ละรอบ:

รูปแบบการวนของไฟ LED

ผลลัพธ์ของสเก็ตช์นี้จะเหมือนกับที่แสดงใน Tutorial 1 ทุกประการ ซึ่งตอนนั้นเราใช้ฟังก์ชันที่อ้างอิงตำแหน่งในอาร์เรย์แบบ absolute ในการควบคุมไฟ LED

สังเกตว่าครึ่งแรกของสเก็ตช์ด้านบนจะดูคล้ายกับสเก็ตช์ใน Tutorial 1 แต่คราวนี้เราใช้การอ้างอิงแบบ relative ผ่านฟังก์ชัน set_bank_pin, set_bank_SIPO และ xfer_bank หมุนเวียนกันไปมา

แล้วครึ่งไหนเวิร์คกว่า? ชัดเจนว่าครึ่งหลังไง เพราะมันรวบการเปลี่ยนสถานะของขาเอาต์พุตทั้งแปดขาให้อยู่ในฟังก์ชันเดียว ซึ่งทำงานกับสถานะแบบ byte ที่แมปไว้ทั้งก้อน แทนที่จะต้องจัดการทีละบิต/ทีละขา

ถ้าอยากให้โค้ดเทพสุดๆ ลองเปลี่ยนครึ่งแรกของสเก็ตช์ให้ใช้ฟังก์ชัน/วิธีเดียวกันกับครึ่งหลังดูสิ แค่เปลี่ยน pattern ของบิตใน byte ที่ส่งไปยัง SIPO (เช่น set_bank_SIPO(bank_id,0, 0b01010101)) ลองทำดูเลย รับรองโค้ดสะอาดตาขึ้นแน่นอน

สุดท้าย ลองเปิด Serial Monitor ดู มันควรจะโชว์ข้อมูลประมาณนี้:

SIPO global values:
pins_per_SIPO = 8
max_SIPOs = 1
bank_SIPO_count = 1
num_active_pins = 8
num_pin_status_bytes = 1
next_free bank = all SIPOs used
Number timers = 0
Bank data:
bank = 0
num SIPOs = 1
latch_pin = 9 clock_pin = 10 data_pin = 8
low_pin = 0 high_pin = 7

แนวทางการต่อยอด

  • Daisy Chaining: ต่อ shift register หลายๆ ตัวเข้าด้วยกันเพื่อควบคุมไฟ LED ได้มากขึ้นอีก
  • Input Filtering: ใช้ shift register ด้านรับสัญญาณเข้า เพื่อลดจำนวนขาที่ต้องใช้

ข้อมูล Frontmatter ดั้งเดิม

title: "เรียน Shift Registers แบบวัยรุ่น! ตอนที่ 2: อย่าจำแอดเดรส จัด Relative Addressing ไปเลย"
description: "มาใช้ `ez_SIPO8_lib` กันต่อในตอนที่ 2 นี้ คราวนี้เราจะมาลุยเรื่อง Relative Addressing แบบตึงๆ เพื่อให้การควบคุม Shift Register มันง่ายและเทพขึ้นอีก! งานง่ายแต่หล่อ รับรองทำเสร็จแน่นอน"
author: "ronbentley1"
category: "Lab Stuff"
tags:
  - "tutorials"
  - "sipo"
  - "74hc595"
  - "shift registers"
views: 561
likes: 0
price: 299
difficulty: "Easy"
components:
  - "1x Shift Register- Serial to Parallel"
  - "1x Arduino UNO"
  - "1x Breadboard (generic)"
  - "8x Resistor 220 ohm"
  - "8x LED (generic)"
  - "1x Jumper wires (generic)"
tools: []
apps:
  - "1x Arduino IDE"
downloadableFiles:
  - "https://projects.arduinocontent.cc/fab0b5b6-d2a4-47a1-aec0-5374c21fd636.ino"
  - "https://projects.arduinocontent.cc/fab0b5b6-d2a4-47a1-aec0-5374c21fd636.ino"
documentationLinks: []
passwordHash: "a8195d34a68ba61afe7bdb33b6f92c1e1f79f0fa243e78e4deebbd9ce1ffa073"
encryptedPayload: "U2FsdGVkX18ujmZJYoBCGz8czgA2qyUFbA/gSjxD+XWUqVhCiud/TR8d9akcVIl8XZTxX6GEtA6gYWWPIN2jTvf5XvOlAE+T14TsrAGW2ys="
seoDescription: "Learn Shift Registers Tutorial 2 on Relative Addressing to help implement the ez_SIPO8_lib library for your Arduino projects."
videoLinks: []
heroImage: "https://cdn.jsdelivr.net/gh/bigboxthailand/arduino-assets@main/images/projects/shift-registers-tutorial-2-relative-addressing-2990f4_cover.jpg"
lang: "en"