MicroPython Programming for ESP32 [9]

<rawat.s>
6 min readApr 30, 2020

--

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍ ‍‍‍‍‍‍ ‍‍

บทความนี้กล่าวถึง การสร้างคลาสในภาษา MicroPython เพื่อนำมาใช้กับโมดูลที่ใช้ชิป TM1638 ควบคุมการทำงานของ LED จำนวน 8 ดวง ตัวแสดงผลแบบ 7-Segment จำนวน 8 หลักและปุ่มกด Push Button จำนวน 8 ตำแหน่ง

TM1638

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍ ‍‍‍‍‍‍ ‍‍

TM1638 เป็นไอซีที่ผลิตโดยบริษัท TITAN Micro Electronics ในประเทศจีน เหมาะสำหรับนำมาใช้ควบคุม LED Array / LED 7-Segment Display และสามารถต่อกับปุ่มกด (Push Buttons) หรือแบบแป้นกดคีย์ (Keypad) ได้

รูปแสดงตำแหน่งขาของไอซี TM1637
ตัวอย่างโมดูล 8-Digit 7-Segment Display / 8-bit Push Buttons & LEDs

การเชื่อมต่อกับไมโครคอนโทรลเลอร์ใช้เพียงสัญญาณ 3 เส้นคือ CLK, DIO และ STB

  • CLK ใช้สำหรับกำหนดจังหวะการส่งข้อมูล โดยใช้วิธีเลื่อนบิตตามขอบขาขึ้น
  • DIO ใช้สำหรับเลื่อนบิต ทำงานได้สองทิศทาง เป็นเอาต์พุตหรืออินพุต การเลื่อนบิตจะเริ่มด้วย LSB First ในทิศทางอินพุตเป็นการรับข้อมูล และใช้ในกรณีที่อ่านค่าดูว่า มีการกดปุ่มหรือไม่ จากขั้นตอน Key Scan
  • STB เป็นขา Strobe หรือ Chip Select ทำงานแบบ Active-Low (STB=Low เริ่มต้นการสื่อสารข้อมูล, STB=High จบการทำงาน) สถานะปรกติ (Idle) จะเป็น High

การควบคุมการทำงานของ TM1638 จะต้องใช้ชุดคำสั่ง (Instruction Set) ซึ่งประกอบด้วย 3 คำสั่งพื้นฐานดังนี้ (Datasheet)

  • CMD1: data instruction set (0x40) สำหรับเริ่มต้นการเขียนหรืออ่านข้อมูล ถ้าเป็นการเขียนข้อมูล ก็จะเป็นการกำหนดค่าบิตให้ LEDs หรือ 7-Segment Display แต่ถ้าเป็นการอ่านข้อมูล จะใช้สำหรับตรวจสอบสถานะการกดปุ่มนอกจากนั้นยังระบุได้อีกว่า เมื่อมีการเขียนหรืออ่านข้อมูลหนึ่งไบต์แล้ว จะให้แอดเดรสเพิ่มโดยอัตโนมัติด้วยหรือไม่ (Auto Address Increment) เป็นต้น
  • CMD2: address instruction set (0xC0) สำหรับกำหนดแอดเดรส (Address) หรือตำแหน่ง (เริ่มต้น) ที่ข้อมูลไบต์จะถูกเขียนลงไป และมีแอดเดรสอยู่ในช่วง 00h .. 1Fh
  • CMD3: display instruction set (0x80) สำหรับกำหนดค่าความสว่าง (Brightness) หรือ เปิด/ปิดการแสดงผล (Display On/Off) เป็นต้น

การใช้คำสั่ง Data Instruction Set (0x40) จะต้องมีการกำหนดค่า Flags ดังนี้

  • Write data to register: 0x00 เพื่อเขียนข้อมูลไปยัง TM1638 ลงในรีจิสเตอร์
  • Read key-scan data: 0x02 เพื่ออ่านข้อมูลที่ได้จากการสแกนอินพุต
  • Auto-address increment: 0x00 เลือกใช้โหมดเพิ่มแอดเดรสโดยอัตโนมัติ
  • Fixed-address: 0x04 ไม่ใช่โหมดเพิ่มแอดเดรส
  • Normal mode: 0x00 ใช้โหมดทำงานปรกติ
  • Test mode: 0x08 ใช้โหมดทดสอบ

ยกตัวอย่างเช่น ถ้าเขียนค่าให้คำสั่ง CMD1 เป็น (0x40 | 0x02) ก็หมายถึง การอ่านค่าจาก Key-Scan Data เป็นต้น

การใช้คำสั่ง CMD1, CMD2 หรือ CMD3 แต่ละคำสั่ง เริ่มต้นจะต้องทำให้ขา STB เป็น LOW (Active) แล้วจึงส่งคำสั่งหรือข้อมูลออกไป และเมื่อส่งเสร็จแล้ว ให้ขา STB เป็น HIGH (Inactive)

ตัวอย่างลำดับการส่งคำสั่งและข้อมูลไปยัง TM1638 (Datasheet)
ตัวอย่างผังวงจร (Schematic)

ตามรูปตัวอย่างผังวงจร จะเห็นได้ว่า ตัวแสดงผล 7-Segment Display มีทั้งหมด 8 หลัก (Digits) แต่ละตัวจะใช้ขาสัญญาณ SEG1/KS1 .. SEG8/KS8 จาก TM1638 มากำหนดสถานะของ LED Segments (a,b,c,d,e,f,g และ dot หรือ DP)

ข้อสังเกต: ขา SEG1/KS1 .. SEG8/KS8 จะใช้สำหรับการอ่านสถานะของปุ่มกดด้วย (KeyScan Input) จำนวน 8 ปุ่ม

ขาสัญญาณ GRID1 .. GRID8 จะทำหน้าที่เป็นตัวควบคุมการทำงานของ 7-Segment แต่ละหลัก (ควบคุมการเปิดหรือปิดการไหลของกระแส) เรียงไปตามลำดับ แบ่งช่วงเวลากัน (Timing Multiplexing)

ขา SEG9 จะใช้ควบคุมการทำงานของ LEDs (Red) อีก 8 ดวง ในขณะที่ SEG10 ไม่ได้ต่อใช้งาน

เนื่องจาก มี SEG1 .. SEG10 ซึ่งหมายถึง จะต้องใช้ 10 บิต ในการการกำหนดสถานะลอจิก ถ้านับให้เป็นไบต์ จึงต้องใช้ข้อมูล 2 ไบต์ (High Byte และ Low Byte) และเนื่องจากมี 8 หลัก จึงมีแอดเดรสสำหรับข้อมูล 16 ตำแหน่ง

  • ข้อมูลที่เขียนลงในแอดเดรสที่เป็นเลขคู่ เช่น 00h, 02h, … จะใช้สำหรับ SEG9 และ SEG10
  • ข้อมูลที่เขียนลงในแอดเดรสที่เป็นเลขคี่ เช่น 01h, 03h, … จะใช้สำหรับกำหนดสถานะของ 7-Segment Display (SEG1 .. SEG8)

ขา K1, K2, K3 เป็นขาสำหรับการสแกนปุ่มกด (ใช้สำหรับ Push Buttons จำนวน 3 แถว ๆ ละ 8 ปุ่ม) แต่สำหรับตามผังวงจรของโมดูลที่ได้เลือกมาใช้งาน จะมีเพียงขา K3 เท่านั้นที่ต่อใช้งาน เนื่องจากมีปุ่มกดแถวเดียว

การตรวจสอบสถานะปุ่มกด จะต้องอ่านข้อมูล 4 ไบต์ และเนื่องจากว่า มีแถวเดียวซึ่งตรงกับ K3 ดังนั้นให้อ่านและตรวจสอบที่บิต B0 และบิตที่ B4 ถ้าบิตมีค่าเป็น 1 แสดงว่า มีการกดปุ่มในขณะนั้น

  • ไบต์ที่ 1 (BYTE1) แสดงสถานะของปุ่มกด 2 ปุ่มที่ตรงกับ KS1 (B0) และ KS2 (B4)
  • ไบต์ที่ 2 (BYTE2) แสดงสถานะของปุ่มกด 2 ปุ่มที่ตรงกับ KS3 (B0) และ KS4 (B4)
  • ไบต์ที่ 3 (BYTE3) แสดงสถานะของปุ่มกด 2 ปุ่มที่ตรงกับ KS5 (B0) และ KS6 (B4)
  • ไบต์ที่ 4 (BYTE4) แสดงสถานะของปุ่มกด 2 ปุ่มที่ตรงกับ KS7 (B0) และ KS8 (B4)
ตารางแสดงลำดับการอ่านข้อมูลจาก TM1638 เพื่อตรวจสอบสถานะปุ่มกด

การคลาส MicroPython สำหรับ TM1638

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍ ‍‍‍‍‍‍ ‍‍

มาดูตัวอย่างการเขียนโค้ด MicroPython เพื่อสร้างคลาส TM1638 สำหรับนำมาใช้งาน มีคำสั่งที่เรียกใช้ เช่น

  • on() เปิดการทำงานของตัวแสดงผล
  • off() ปิดการทำงานของตัวแสดงผล
  • brightness() กำหนดระดับความสว่างของ LEDs เลือกได้ 0..7 (จากต่ำสุดถึงสูงสุด)
  • write() ใช้สำหรับเขียนคำสั่งและข้อมูลเพื่อกำหนดสถานะการทำงานของ LED หรือ 7-Segment ตามแอดเดรสที่ระบุ
  • led() กำหนดสถานะ on/off ของ LED ตามตำแหน่งที่ระบุ (pos=0..7)
  • segments() กำหนดข้อมูลสำหรับแสดงสถานะของ 7-Segment Display ในตำแหน่งที่ระบุ (pos=0..7)
  • read_buttons() อ่านข้อมูลสถานะของปุ่มกดทั้ง 8 ปุ่ม ได้ค่ากลับคืนเป็นไบต์
# file: tm1638.py
from micropython import const
from machine import Pin
from time import sleep_us, sleep_ms
DIGITS = bytearray(b'\x3F\x06\x5B\x4F\x66\x6D\x7D\x07\x7F\x6F')class TM1638():
_CMD1 = const(0x40) # data command
_CMD2 = const(0xC0) # address command
_CMD3 = const(0x80) # display control command
_KEY_READ = const(0x02) # read key scan data flag for CMD1
_DISP_ON = const(0x08) # display on flag for CMD3
def __init__(self, stb, clk, dio, brightness=7):
self._stb = stb
self._clk = clk
self._dio = dio
self._brightness = brightness # 0..7
self._disp_on = _DISP_ON
self._clk.init( Pin.OUT, value=1 )
self._dio.init( Pin.OUT, value=0 )
self._stb.init( Pin.OUT, value=1 )
self.clear()
def _write_byte(self, b):
for i in range(8):
self._clk(0)
self._dio((b >> i) & 1) # LSB first
self._clk(1)
def _cmd1(self):
self._stb(0) # start
# write mode, auto-increment addressing, normal mode
self._write_byte(_CMD1)
self._stb(1) # stop
def _cmd2(self, data, addr=0):
self._stb(0) # start
# set address start, followed by one data byte
self._write_byte( _CMD2 | (addr & 0xf) )
if isinstance(data,list):
for b in data:
self._write_byte( b )
sleep_us(1)
else:
self._write_byte( data )
self._stb(1) # stop
def _cmd3(self):
self._stb(0) # start
# display command: display on, set brightness
self._write_byte( _CMD3 | self._disp_on | self._brightness )
self._stb(1) # stop
def on(self):
self._disp_on = _DISP_ON
self._cmd3()
def off(self):
self._disp_on = 0
self._cmd3()
def brightness(self, val=7):
self._brightness = val & 0b111
self._cmd3()
def clear(self):
self.write( 16*[0], 0 )
def write(self, data, addr=0):
self._cmd1()
self._cmd2( data, addr )
self._cmd3()
def led(self, pos, value):
self._cmd1()
self._cmd2( value, (pos << 1) | 1 )
def segment(self, pos, value):
self._cmd1()
self._cmd2( value, (pos << 1) )

def read_buttons(self):
value = 0
self._stb(0)
self._write_byte( _CMD1 | _KEY_READ )
self._dio.init( Pin.IN, Pin.PULL_UP )
for j in range(4): # read 4 bytes
temp = 0
for i in range(4): # read nibble
self._clk(0)
if self._dio.value():
temp = 0x08
self._clk(1)
for i in range(4): # read nibble
self._clk(0)
if self._dio.value():
temp = 0x80
self._clk(1)
value = (value >> 1) | temp
self._dio.init( Pin.OUT, 0 )
self._stb(1)
return value

ตัวอย่าง: แสดงค่า Pi บน 7-Segment Display

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍ ‍‍‍‍‍‍ ‍‍

ตัวอย่างแรกที่สาธิตการใช้คลาส TM1638 เป็นการแสดงตัวเลขของค่า Pi จำนวน 7 หลักหลังจุดทศนิยม (Decimal Point)

ในการต่อวงจรระหว่าง ESP32 กับ TM1638 ก็ใช้แรงดันไฟเลี้ยง 3.3V และ GND จากบอร์ด ESP32 และในตัวอย่างนี้ได้เลือกใช้ขา GPIO23=STB, GPIO22=CLK และ GPIO21=DIO ตามลำดับ

import tm1638
from machine import Pin
import utime as time
import math
tm = tm1638.TM1638(stb=Pin(23), clk=Pin(22), dio=Pin(21))
tm.clear()
tm.brightness(4)
x = int(math.pi*10e6) # calculate Pi value (truncated)
for i in range(8):
value = tm1638.DIGITS[ x%10 ]
if i==7:
value |= 0x80 # show dot (DP)
tm.segment( 7-i, value )
x //= 10
time.sleep(3.0)
tm.off()
del tm

ตัวอย่าง: อ่านค่าปุ่มกดและแสดงค่าตัวนับในแต่ละหลักของ 7-Segment Display

‍‍‍‍‍‍ ‍‍ ‍‍‍‍‍‍ ‍‍‍‍‍‍ ‍‍

ตัวอย่างที่สอง สาธิตการเปิดหรือปิด LED แต่ละดวงเรียงไปตามลำดับ ถัดไปทำให้ 7-Segment Display ทั้ง 8 ตำแหน่ง แสดงตัวเลขเริ่มต้นเป็น 0 ทุกตำแหน่ง

เมื่อมีการกดปุ่มใดปุ่มหนึ่ง จะทำให้ LED ที่ตรงกับตำแหน่งของปุ่มกดสว่างขึ้น แล้วจะเพิ่มค่าตัวเลขในตำแหน่งดังกล่าวทีละหนึ่ง ถ้ากดปุ่มค้าง จะทำให้ตัวเลขเพิ่มขึ้นต่อเนื่อง และถ้าเพิ่มให้ถึง 9 ถัดไปกลับไปเริ่มที่ 0 ใหม่

# file: tm1638_demo.py
import tm1638
from machine import Pin
import utime as time
tm = tm1638.TM1638(stb=Pin(23), clk=Pin(22), dio=Pin(21))
tm.on()
tm.brightness(7)
tm.clear()
for i in range(8): # 0,1,2...,7
tm.led(i, 1) # turn on LED at position i
time.sleep_ms(50)
for i in range(7,-1,-1): # 7,6,...,0
tm.led(i, 0) # turn off LED at position i
time.sleep_ms(50)
values = 8*[0]
try:
while True:
# read input buttons
input_bits = tm.read_buttons()
for i in range(8): # check input button
# if the button is pressed, increment the values[i]
if (input_bits&1) == 1:
values[i] = (values[i]+1) % 10
# update the LED status (turn on if pressed)
tm.led( i, (input_bits&1) )
# shift bits to the right
input_bits >>= 1
# update 7-segment status
tm.segment( i, tm1638.DIGITS[ values[i] ] )
time.sleep_ms(150)
except KeyboardInterrupt:
pass
finally:
tm.off()
print('Done')
ตัวอย่างอุปกรณ์ที่ได้นำมาทดสอบ

โดยสรุป เราได้เรียนรู้หลักการทำงานของโมดูล TM1638 ในเบื้องต้น และได้เห็นตัวอย่างการสร้างคลาสในภาษา MicroPython และตัวอย่างสาธิตการใช้บอร์ด ESP32 ร่วมกับโมดูล TM1638 ที่มี LEDs / 7-Segments / Push Buttons ขนาด 8 บิต … หวังว่า จะเป็นประโยชน์ต่อผู้ที่สนใจเรียนรู้ภาษา Python แบบสัมผัสฮาร์ดแวร์นะครับ

--

--

<rawat.s>
<rawat.s>

Written by <rawat.s>

I'm Thai and working in Bangkok/Thailand.

No responses yet