ใบงานที่2 7 Segment

ใบงานที่ 2

7 Segment

ผู้จัดทำ

นาย สราวุธ ชอบเพื่อน 021
นาย กฤษณะ รักธรรม   001

หลักการทำงาน

ไมโครคอนโทรลเลอร์โดยส่วนใหญ่แล้วมีขาสำหรับเป็น Digital I/O ที่จำกัด การจะนำไปกับงานที่ต้องใช้พอร์ตเยอะๆ โดยตรง ไม่ค่อยที่จะเหมาะนัก เนื่องจากจะทำให้เปลืองพอร์ต อีกทั้งยังส่งผลไม่ให้ต่อร่วมกับอุปกรณ์อื่นๆได้อีกด้วย
ไอซี 74HC595 เป็นไอซีที่ออกแบบมาสำหรับการใช้งานพอร์ตเยอะๆ เช่น การนำไปขับ 7 Segment หรือ LED Dot Matrix หรือป้ายไฟต่างๆ โดยใช้สายต่อเข้ากับพอร์ตของไมโครคอนโทรลเลอร์เพียง 3 เส้นเท่านั้น อีกทั้งการสั่งงานยังง่ายมากๆอีกด้วย

รู้จักกับ 74HC595

ไอซี 74HC595 เป็นไอซีเลื่อนบิต เมื่อมีการป้อนข้อมูลเข้าไปใหม่ บิตจะถูกเลื่อนไปตามข้อมูลที่ป้อน มีขาทั้งหมด 16 ขา เป็นขาเอาต์พุตที่ควบคุมได้ทั้งหมด 8 ขา ตั้งแต่ Q0 Q1 Q2 ... Q7 มีขา 3 ขาสำหรับการควบคุมขา Q0 - Q7
  • ขา ST_CP เป็นขาควบคุมจังหวะการส่งข้อมูล เมื่อไม่มีการส่งข้อมูลจะให้สถานะขานี้เป็น HIGH แต่เมื่อมีการส่งข้อมูลจะให้ขานี้มีสถานะเป็น LOW จนกว่าจะหยุดส่งข้อมูลจึงให้ขานี้มีสถานะกลับมาเป็น HIGH อีกครั้ง
  • ขา SH_CP เทียบได้กับขา Clock เป็นขาที่จะต้องป้อนสัญญาณพัลส์เข้าไปเพื่อควบคุมการรับข้อมูลเข้าไอซี โดยการป้อนสัญญาณจะต้องสัมพันธกับการป้อนข้อมูลในแต่ละบิต
  • ขา DS เป็นขาสำหรับป้อนข้อมูลเข้าไปทีละบิต
และมีอีก 2 ขาสำหรับการป้อนข้อมูลลอจิกคงที่
  • ขา MR ต่อให้มีสถานะเป็น HIGH หรือต่อเข้า +VCC
  • ขา OE ต่อให้มีสถานะเป็น LOW หรือต่อเข้า GND

การควบคุมการทำงานไอซี 74HC595

การควบคุมการส่งข้อมูลเพื่อเปลี่ยนแปลง Q0 - Q7 มีขั้นตอนดังนี้
Step 1 เซ็ตให้ขา ST_CP มีสถานะเป็น LOW
Step 2 เซ็ตให้ขา DS เป็นข้อมูลบิตนั้นๆ
Step 3 เซ็ตให้ขา SH_CP มีสถานะเป็น HIGH
Step 4 หน่วงเวลา
Step 5 เซ็ตให้ขา SH_CP มีสถานะเป็น LOW
Step 6 หน่วงเวลา
Step 7 เริ่มกลับไปทำ Step 2 จนครบหมดทุกบิต
Step 8 เซ็ตให้ขา ST_CP มีสถานะเป็น HIGH
Timing Diagram
การส่งข้อมูลแต่ละบิตเข้าไปใหม่จะเลื่อนบิตเดิมไปทางขวาเรื่อยๆ หากส่งไปทั้งหมด 8 บิตจะทำให้ข้อมูลเก่าหายไปจนหมด และการเปลี่ยนแปลงผลจะเห็นได้ก็ต่อเมื่อ ST_CP มีสถานะกลับมาเป็น HIGH เท่านั้น

การใช้งานจริงกับหลอด LED

ในบทความนี้จะเลือกใช้บอร์ด Arduino ในการสาธิตการทำงาน สามารถต่อวงจรได้ดังนี้
ใน Arduino จะมีฟังก์ชั่น shiftOut() สำเร็จรูปอยู่แล้ว ทำให้สามารถข้ามขั้นตอนตั้งแต่ Step 2 ไปจนถึง Step 7 ได้เลย ทำให้ส่วนโค้ดจริงๆที่ใช้สำหรับส่งข้อมูลไปให้ไอซีมีแค่ส่วนนี้ (ผมเขียนเป็นฟังก์ชั่นไว้จะเรียกใช้งานได้ง่าย)
void DataOut(byte data) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, data);
  digitalWrite(latchPin, HIGH);
}
ตัวอย่างโค้ดด้านล่างนี้เป็นโค้ดไฟวิ้งจากขวาไปซ้ายครั้งละดวง โดยใช้ชนิดตัวแปรเป็น byte แล้วเลื่อนบิตไปเรื่อยๆ
int latchPin = 8; // ST_CP
int clockPin = 12; // SH_CP
int dataPin = 11; // DS

byte tmp;

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  digitalWrite(latchPin, HIGH);
}

void loop() {
  if (tmp==0||tmp==0x80) tmp=0x01;
  else tmp=tmp<<1;
  DataOut(tmp);
  delay(500);
}

void DataOut(byte data) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, data);
  digitalWrite(latchPin, HIGH);
}

การใช้งานจริงกับ 7 Segment

ไอซี 74HC595 เหมาะมากสำหรับการใช้งานกับ 7 Segment ถึงจะใช้งานได้ยากกว่าไอซีแปลงรหัส BCD แต่ไอซี 74HC595 ใช้สายน้อยกว่า
โค้ดด้านล่างนี้เป็นโค้ดที่ใช้หลักการคล้ายๆกับบทความที่แล้ว การใช้งาน 7 Segment กับ Arduino ตอนที่ 1 7 Segment หลักเดียว เพียงแต่คำสั่งที่ใช้ควบคุม 7 Segment เปลี่ยนจากการใช้รีจิสเตอร์ PORTD เป็นใช้ฟังก์ชั่น DataOut() ส่งไปให้ไอซี 74HC595 ขับแทน
int latchPin = 8; // ST_CP
int clockPin = 12; // SH_CP
int dataPin = 11; // DS

int num[] = { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F };

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  digitalWrite(latchPin, HIGH);
}

void loop() {
  for (int i=0;i<10;i++) {
    DataOut(~num[i]);
    delay(500);
  }
}

void DataOut(byte data) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, data);
  digitalWrite(latchPin, HIGH);
}

การใช้งาน 74HC595 หลายตัว

บางครั้งพอร์ตที่เราต้องการจะใช้งานก็มีมากจนไอซี 74HC595 ตัวเดียวไม่พอใช้ จึงทำให้จะต้องใช้หลายตัวเพื่อให้มีขาใช้งานมากขึ้น
การใช้งาน 74HC595 หลายตัวสามารถทำได้โดยการต่อสาย SH_CP และ ST_CP ร่วมกับตัวแรก แต่ขา DS ให้ต่อเข้ากับขา Q7' ของตัวก่อนหน้านี้ (ดูรูปวงจรประกอบเพื่อให้เข้าใจได้ง่ายขึ้น)
การส่งข้อมูล จากเดิมที่ส่งข้อมูลไป 8 บิต ก็ต้องส่งข้อมูลไป 8 บิต * จำนวนไอซี เช่น ใช้ไอซี 74HC595 จำนวน 3 ตัว เมื่อส่งข้อมูลจะต้องส่งไปทั้งหมด 8 * 3 = 24 บิต

การใช้งานจริงกับหลอด LED โดยใช้ไอซี 74HC595 หลายตัว

ในตัวอย่างนี้จะใช้ไอซี 74HC595 จำนวน 2 ตัวในการขับ LED จำนวน 16 ดวงให้วิ้งตั้งแต่ดวงแรกไปจนถึงดวงสุดท้ายแล้วกลับมาเริ่มต้นใหม่ โดยเปลี่ยนโค้ดส่วนที่ใช้ส่งข้อมูลไปให้ไอซี 74HC595 เป็นดังนี้
void DataOut(unsigned int data) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, data>>8);
  shiftOut(dataPin, clockPin, MSBFIRST, data&0xFF);
  digitalWrite(latchPin, HIGH);
}
จากโค้ด จะเห็นว่าได้มีการใช้คำสั่ง shiftOut() จำนวน 2 ครั้ง หมายถึงการส่งข้อมูล 8 บิตหน้าให้ก่อน แล้วจึงใช้คำสั่ง shiftOut() อีกครั้งในการส่งอีก 8 บิตหลัง หรือถ้าให้เข้าใจง่ายๆ shiftOut() ตัวแรกควบคุม 74HC595 ตัวที่ 2 ส่วน shiftOut() ตัวที่ 2 ก็ควบคุม 74HC595 ตัวแรก หากใช้ไอซี 74HC595 หลายตัวมากขึ้น ก็ต้องใช้คำสั่ง shiftOut() มากขึ้น
โค้ดไฟวิ้ง 16 ดวงโดยใช้ 74HC595 2 ตัวขับแบบเต็มๆมีดังนี้
int latchPin = 8; // ST_CP
int clockPin = 12; // SH_CP
int dataPin = 11; // DS

int tmp=0;

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  digitalWrite(latchPin, HIGH);
}

void loop() {
  if (tmp==0||tmp==0x8000) tmp=0x01;
  else tmp=tmp<<1;
  DataOut(tmp);
  delay(100);
}

void DataOut(unsigned int data) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, data>>8);
 shiftOut(dataPin, clockPin, MSBFIRST, data&0xFF);
  digitalWrite(latchPin, HIGH);
}

ข้อมูลเพิ่มเติม

การต่อใช้งาน 74HC595 ร่วมกันหลายตัวต้องนำการกระทำระดับบิตเข้ามาเกี่ยวข้อง หากเลือกใช้ตัวแปรในการเก็บข้อมูลการติด-ดับของ LED เนื่องจากคำสั่ง shiftOut() สามารถส่งข้อมูลออกไปได้เพียงครั้งละ 8 บิต หรือ 1 ไบต์ จากในหัวข้อ การใช้งานจริงกับหลอด LED โดยใช้ไอซี 74HC595 หลายตัว จะเห็นได้ว่าผมได้เลือกใช้ตัวแปรชนิด unsigned int มาเก็บข้อมูล เนื่องจากตัวแปรชนิดนี้สามารถเก็บข้อมูลได้ 2 ไบต์ หรือเทียบเท่ากับการเก็บข้อมูลให้ 74HC595 จำนวน 2 ตัว
ส่งข้อมูลบิตสูงออกไปก่อนเสมอ หากดูจาก Timing Diagram จะเห็นได้ว่าเราจะต้องส่งข้อมูลบิตสูงที่สุดออกไปก่อน เป็นลักษณะการส่งข้อมูลลำดับบิตที่สูงที่สุด ลงมาจนถึงลำดับบิตที่ต่ำที่สุด ซึ่งการส่งข้อมูลบิตที่อยู่ในลำดับที่ 9-16 ไปก่อนทำได้โดยการเลื่อนให้บิตไปอยู่ลำดับที่ 1 - 8 แทน โดยใช้เครื่องหมายกระทำระดับบิต เลื่อนบิตไปทางขวา 8 ตัว (data>>8)
ลำดับบิต161514131211109  87654321
ข้อมูล (data)1111111111001100
เลื่อนบิตไป 8 ลำดับ0000000011111111
ส่งข้อมูลบิตต่ำตามออกไป เมื่อส่งข้อมูลบิตสูงตั้งแต่บิตที่ 9 - 16 ออกไปแล้ว ต่อมาจึงส่งบิต 1 - 8 ตามออกไป แต่ในตัวแปร data มีข้อมูลแบบ 16 บิตอยู่ แต่ฟังก์ชั่น shiftOut() รองรับได้แค่ 8 บิต ดังนั้นจึงต้องตัดบิตที่ 9-16 ทิ้งไป เหลือแค่บิตที่ 1-8 ไว้ แล้วจึงส่งไปให้ฟังก์ชั่น shiftOut() ส่งข้อมูลออกไป , การตัดบิตที่ 9 - 16 ทิ้งไปทำได้ด้วยการนำมา and 0xFF (data&0xFF)
ลำดับบิต161514131211109  87654321
ข้อมูล (data)1111111111001100
and 0xFF0000000011111111
ผลลัพธ์0000000011001100
ในไมโครคอนโทรลเลอร์อื่นๆที่ไม่มีคำสั่ง shiftOut() ก็สามารถเขียนคำสั่ง shiftOut() เองได้ โดยอ้างอิงจาก Timing Diagram เป็นหลัก โค้ดด้านล่างนี้เป็นตัวอย่างของโค้ดไฟวิ้ง 16 ดวงใช้ 74HC595 เป็นตัวขับ ใช้ไมโครคอนโทรลเลอร์ PIC18F4550 ใช้ IDE เป็น MPLAB และใช้คอมไพลเลอร์เป็น C18
#include "p18f4550.h"
#include "delays.h"

/*
----- MAP ----
latchPin -> ST_CP
clockPin -> SH_CP
dataPin -> DS
*/

#define latchPin LATDbits.LATD2
#define clockPin LATDbits.LATD0
#define dataPin LATDbits.LATD1

void delay_ms(int ms) {
  while(ms--)
    Delay1KTCYx(5); // 20MHz
}

int ibit=0;
void shiftOut(char data) {
  for (ibit=7;ibit>=0;ibit--) {
    dataPin=(data>>ibit)&0x01;
    delay_ms(1);
    clockPin=1;
    delay_ms(1);
    clockPin=0;
  }
  dataPin=0;
}

void DataOut(unsigned int data) {
  latchPin=0;
  shiftOut(data>>8);
  shiftOut(data&0xFF);
  latchPin=1;
}

int tmp=0;

void main() {
  TRISD=0;
  LATD=0;
  latchPin=1;
  while(1) {
    if (tmp==0||tmp==0x8000) tmp=0x01;
    else tmp=tmp<<1;
    DataOut(tmp);
    delay_ms(100);
  }
}
ทุกคนคงรู้จัก 7 Segment กันพอสมควรแล้ว เพราะเราต้องเคยเห็นในชีวิตประจำวันมาบ้าง
7 Segment ก็คืออุปกรณ์แสดงผลตัวเลข 0-9 หรือตัวอักษรบางตัว โดยใช้หลอดไฟทั้ง 7 หลอดของมัน ,การติดหรือดับของหลอดไฟแต่ละ Segment ทำให้เกิดภาพตัวเลขหรือตัวอักษรต่างๆตามที่เราเห็นนั่นเอง
ซึ่งจริงๆแล้ว 7 Segment ก็คือ LED 7 ดวงนี่เอง เพียงแต่มันถูกรวมอยู่ในตัวถังเดียวกัน เพราะฉะนั้น การใช้งานก็จะต้องมีการจ่ายไฟคล้ายกับ LED ทั่วไป คือมีไฟ + และไฟ - หรือ GND แต่ทั้งนี้ เพื่อไม่ให้ใช้จำนวนขาที่มากเกินไป จึงมีการออกแบบขาร่วม (Common) ขึ้นมาด้วย เพื่อประหยัดจำนวนขาใช้งานไปได้ถึงเท่าตัว โดยแบ่งออกเป็น 2 ประเภท
• Common Anode หรือขาบวกร่วม
• Common Cathode หรือขาลบร่วม

จากภาพแสดงให้เห็นถึงหลักการทำงาน 7 Segment โดยไฟ LED ดวงที่ 1 - 7 (a-g) ทำหน้าที่แสดงตัวเลขหรือตัวอักษร ส่วนดวงที่ 8 (h) ทำหน้าที่แสดงจุดทศนิยม
โดยแบบ Common Anode และ Common Cathode มีความแตกต่างกันคือ ขาร่วมของ Common Anode จะเป็นไฟบวก และขาร่วมของ Common Cathode จะเป็นไฟลบหรือ GND ,ซึ่งความแตกต่างนี้เอง เป็นผลให้เราควบคุมการติดดับของ LED แต่ละดวงต่างกันทั้ง 2 ชนิดด้วย

การควบคุมการติดดับของ LED แต่ละ Segment


การควบคุม LED แต่ละ Segment ขึ้นอยู่กับการจ่าไฟไปยัง Segment นั้นๆ แต่ไม่ได้หมายความว่า
เมื่อจ่ายไฟบวกไปยัง Segment ใดๆ แล้ว Segment นั้นไฟจะติด เพราะมันขึ้นอยู่กับว่า 7 Segment
 ของเราเป็นแบบขาบวกร่วม หรือลบร่วม

บบ Common Anode เมื่อต้องการให้ไฟที่ Segment ใดๆติด จะต้องให้ขาของ Segment นั้นเป็น 0 หรือ LOW
แบบ Common Cathode เมื่อต้องการให้ไฟที่ Segment ใดๆติด จะต้องให้ขาของ Segment นั้นเป็น 1 หรือ HIGH
เนื่องจาก ตามหลักการที่ว่า กระแสไฟฟ้าย่อมไหลจาก ศักย์สูง ไปยัง ศักย์ต่ำ นั่นหมายถึง การที่ไฟ LED จะติดได้ จะต้องเกิดจากการที่มีกระแสไฟฟ้าไหลผ่านตัวมัน จากจุดที่มีศักย์ไฟฟ้าสูงกว่า ไปยังจุดที่มีศักย์ไฟฟ้าต่ำกว่า หรือที่เรียกว่าความต่างศักย์นั่นเอง เพราะฉะนั้นขาร่วมแบบ "บวกร่วม" จะมีศักย์ไฟฟ้าที่สูงกว่า จึงต้องทำให้ปลายอีกข้างเป็น LOW เพื่อให้กระแสไหลผ่านได้ ส่วนขาร่วมแบบ "ลบร่วม" ก็จะตรงกันข้ามนั่นเอง
โดยค่าความต่างศักย์ จะต้องไม่เกินค่าที่ LED นั้นๆทนได้ ซึ่งในบทเรียนนี้ เราจะยกตัวอย่างเป็น 7 Segment LED สีแดง ซึ่งจะทนความต่างศักย์ได้ไม่เกิน 2.3 V

เครดิต:http://commandronestore.com/learning/7segment.php

รูปวงจร



วีดีโอการทดสอบ


โค้ด 


int LED1 = 2;
int LED2 = 3;
int LED3 = 4;
int LED4 = 5;
void setup() {
  // initialize serial communication:
  Serial.begin(9600);
  pinMode (LED1,OUTPUT);
  pinMode (LED2,OUTPUT);
  pinMode (LED3,OUTPUT);
  pinMode (LED4,OUTPUT);
}

void loop() {
  // read the sensor:
  int sensorReading = analogRead(A0);
  // map the sensor range to a range of four options:
  int range = map(sensorReading, sensorMin, sensorMax, 0, 3);

  // do something different depending on the range value:
  switch (range) {
    case 0:    // your hand is on the sensor
      Serial.println("dark");
      digitalWrite (LED1,HIGH);
      digitalWrite (LED2,LOW);
      digitalWrite (LED3,LOW);
      digitalWrite (LED4,LOW);
      break;
    case 1:    // your hand is close to the sensor
      Serial.println("medium");
      digitalWrite (LED2,HIGH);
      digitalWrite (LED3,LOW);
      digitalWrite (LED1,LOW);
      digitalWrite (LED4,LOW);
      break;
    case 2:    // your hand is a few inches from the sensor
      Serial.println("dim");
      digitalWrite (LED3,HIGH);
      digitalWrite (LED1,LOW);
      digitalWrite (LED2,LOW);
      digitalWrite (LED4,LOW);
      break;
    case 3:    // your hand is nowhere near the sensor
      Serial.println("bright");
      digitalWrite (LED4,HIGH);
      digitalWrite (LED2,LOW);
      digitalWrite (LED3,LOW);
      digitalWrite (LED1,LOW);
      break;
  }
  delay(1);        // delay in between reads for stability
}




                                                  ดาวโหลด

ความคิดเห็น

แสดงความคิดเห็น

บทความที่ได้รับความนิยม