|
@@ -0,0 +1,157 @@
|
|
|
|
|
+import rp2
|
|
|
|
|
+from machine import Pin
|
|
|
|
|
+import time
|
|
|
|
|
+
|
|
|
|
|
+class TM1637:
|
|
|
|
|
+ # 割り当て可能なステートマシンID管理
|
|
|
|
|
+ _available_ids = list(range(8))
|
|
|
|
|
+
|
|
|
|
|
+ # セグメントデータ定義 (Cコードの digit_seg / letter_seg に対応)
|
|
|
|
|
+ _DIGIT_SEG = [0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f]
|
|
|
|
|
+ _LETTER_SEG = {
|
|
|
|
|
+ 'A': 0x77, 'b': 0x7c, 'C': 0x39, 'd': 0x5e, 'E': 0x79, 'F': 0x71,
|
|
|
|
|
+ 'G': 0x3d, 'H': 0x76, 'i': 0x04, 'J': 0x0e, 'L': 0x38, 'n': 0x54,
|
|
|
|
|
+ 'o': 0x5c, 'P': 0x73, 'q': 0x67, 'r': 0x50, 'S': 0x6d, 't': 0x78,
|
|
|
|
|
+ 'u': 0x1c, 'y': 0x6e, '-': 0x40, ' ': 0x00
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ # PIOプログラム定義
|
|
|
|
|
+ @rp2.asm_pio(
|
|
|
|
|
+ out_init=(rp2.PIO.IN_HIGH),
|
|
|
|
|
+ set_init=(rp2.PIO.IN_HIGH),
|
|
|
|
|
+ sideset_init=(rp2.PIO.OUT_HIGH),
|
|
|
|
|
+ out_shiftdir=rp2.PIO.SHIFT_RIGHT,
|
|
|
|
|
+ autopull=False,
|
|
|
|
|
+ fifo_join=rp2.PIO.JOIN_TX
|
|
|
|
|
+ )
|
|
|
|
|
+ def _tm1637_pio():
|
|
|
|
|
+ wrap_target()
|
|
|
|
|
+ # --- Start Condition ---
|
|
|
|
|
+ pull(block)
|
|
|
|
|
+ set(pindirs, 0b00) # SDA/SCL input
|
|
|
|
|
+ nop() [7]
|
|
|
|
|
+ set(pindirs, 0b01) # SDA output/SCL input
|
|
|
|
|
+ set(pins, 0) # SDA is Low
|
|
|
|
|
+ nop() [7]
|
|
|
|
|
+ set(pindirs, 0b11) # SDA/SCL output
|
|
|
|
|
+ set(x, 7) .side(0) # CLK is Low
|
|
|
|
|
+ jmp("output")
|
|
|
|
|
+
|
|
|
|
|
+ label("byte_loop")
|
|
|
|
|
+ set(x, 7) .side(0) # CLK is Low
|
|
|
|
|
+ pull(block)
|
|
|
|
|
+
|
|
|
|
|
+ label("output")
|
|
|
|
|
+ nop() [3]
|
|
|
|
|
+ out(pins, 1) [4] # LSB output
|
|
|
|
|
+ nop() .side(1) # CLK is High
|
|
|
|
|
+ nop() [7]
|
|
|
|
|
+ nop() .side(0) # CLK is Low
|
|
|
|
|
+ jmp(x_dec, "output")
|
|
|
|
|
+
|
|
|
|
|
+ # --- ACK Check ---
|
|
|
|
|
+ set(pindirs, 0b10) [7] # SDA input/SCL output
|
|
|
|
|
+ nop() .side(1) # SCL = High
|
|
|
|
|
+ jmp(pin, "nack") # SDA High (NACK) なら分岐
|
|
|
|
|
+ jmp("ack")
|
|
|
|
|
+
|
|
|
|
|
+ label("nack")
|
|
|
|
|
+ irq(rel(0)) # NACK通知
|
|
|
|
|
+
|
|
|
|
|
+ label("ack")
|
|
|
|
|
+ nop() [6]
|
|
|
|
|
+ nop() .side(0) # CLK is Low (ACK cycle end)
|
|
|
|
|
+ set(pindirs, 0b11) [7] # SDA/SCL Output
|
|
|
|
|
+ out(x, 1) # 9bit目を読み取って判定
|
|
|
|
|
+ jmp(not_x, "byte_loop") # 0なら次のバイトへ
|
|
|
|
|
+
|
|
|
|
|
+ # --- Stop Condition ---
|
|
|
|
|
+ set(pins, 0) [7] # SDA is Low
|
|
|
|
|
+ nop() .side(1) # SCL is High
|
|
|
|
|
+ nop() [7]
|
|
|
|
|
+ set(pindirs, 0b00) [7] # SDA/SCL Input (STOP)
|
|
|
|
|
+ wrap()
|
|
|
|
|
+
|
|
|
|
|
+ def __new__(cls, *args, **kwargs):
|
|
|
|
|
+ if not cls._available_ids:
|
|
|
|
|
+ raise RuntimeError("Maximum TM1637 instances (8) reached")
|
|
|
|
|
+ instance = super().__new__(cls)
|
|
|
|
|
+ instance.sm_id = cls._available_ids.pop(0)
|
|
|
|
|
+ return instance
|
|
|
|
|
+
|
|
|
|
|
+ def __init__(self, sda_pin, columns=6, contrast=3):
|
|
|
|
|
+ # SDAの次のピンをSCLとする仕様を再現 (sda_base_pin + 1)
|
|
|
|
|
+ self.sda_pin = Pin(sda_pin, Pin.IN, pull=Pin.PULL_UP)
|
|
|
|
|
+ self.scl_pin = Pin(sda_pin + 1, Pin.IN, pull=Pin.PULL_UP)
|
|
|
|
|
+ self.columns = columns
|
|
|
|
|
+
|
|
|
|
|
+ # PIO初期化 (2MHz)
|
|
|
|
|
+ self.sm = rp2.StateMachine(
|
|
|
|
|
+ self.sm_id,
|
|
|
|
|
+ self._tm1637_pio,
|
|
|
|
|
+ freq=2000000,
|
|
|
|
|
+ sideset_base=self.scl_pin,
|
|
|
|
|
+ set_base=self.sda_pin, # SDA(bit0) & SCL(bit1) をまとめて制御
|
|
|
|
|
+ out_base=self.sda_pin,
|
|
|
|
|
+ jmp_pin=self.sda_pin
|
|
|
|
|
+ )
|
|
|
|
|
+ self.sm.active(1)
|
|
|
|
|
+
|
|
|
|
|
+ # 初期表示設定
|
|
|
|
|
+ self.clear()
|
|
|
|
|
+ self.set_contrast(contrast)
|
|
|
|
|
+
|
|
|
|
|
+ def _send_cmd(self, data, keep_going=False):
|
|
|
|
|
+ # 9ビット目に「終了(1)」か「継続(0)」のフラグを立てる
|
|
|
|
|
+ val = (data & 0xFF) | (0 if keep_going else 0x100)
|
|
|
|
|
+ self.sm.put(val)
|
|
|
|
|
+
|
|
|
|
|
+ def set_contrast(self, level):
|
|
|
|
|
+ # 0x88 (Display ON) | 0~7
|
|
|
|
|
+ cmd = 0x88 | (level & 0x07)
|
|
|
|
|
+ self._send_cmd(cmd)
|
|
|
|
|
+
|
|
|
|
|
+ def _chr_to_seg(self, char):
|
|
|
|
|
+ if '0' <= char <= '9':
|
|
|
|
|
+ return self._DIGIT_SEG[int(char)]
|
|
|
|
|
+ return self._LETTER_SEG.get(char, 0x00)
|
|
|
|
|
+
|
|
|
|
|
+ def show_str(self, s):
|
|
|
|
|
+ # 文字列を解析してセグメントデータのリストを作成
|
|
|
|
|
+ segments = []
|
|
|
|
|
+ i = 0
|
|
|
|
|
+ s_len = len(s)
|
|
|
|
|
+
|
|
|
|
|
+ # カラム数または文字列が終わるまでループ
|
|
|
|
|
+ while i < s_len and len(segments) < self.columns:
|
|
|
|
|
+ char = s[i]
|
|
|
|
|
+ base_seg = self._chr_to_seg(char)
|
|
|
|
|
+
|
|
|
|
|
+ # 次の文字が存在し、かつドットかコロンの場合
|
|
|
|
|
+ if (i + 1) < s_len and s[i + 1] in ('.', ':'):
|
|
|
|
|
+ base_seg |= 0x80 # 8ビット目(ドット)を立てる
|
|
|
|
|
+ i += 1 # ドット文字分インデックスを進める(スキップ)
|
|
|
|
|
+
|
|
|
|
|
+ segments.append(base_seg)
|
|
|
|
|
+ i += 1
|
|
|
|
|
+
|
|
|
|
|
+ # 2. 生成したデータを出力
|
|
|
|
|
+ # データセット(自動アドレスインクリメント)
|
|
|
|
|
+ self._send_cmd(0x40) # TM1637_AUTOINC
|
|
|
|
|
+ # アドレス設定 (0)
|
|
|
|
|
+ self._send_cmd(0xC0, keep_going=True) # TM1637_ADDRSET
|
|
|
|
|
+
|
|
|
|
|
+ # リストの内容を送信
|
|
|
|
|
+ for j, seg in enumerate(segments):
|
|
|
|
|
+ # 最後かどうかを判定 (リストの最後 かつ カラム最大)
|
|
|
|
|
+ is_last = (j == len(segments) - 1)
|
|
|
|
|
+ self._send_cmd(seg, keep_going=not is_last)
|
|
|
|
|
+
|
|
|
|
|
+ def clear(self):
|
|
|
|
|
+ self.show_str(' ' * self.columns)
|
|
|
|
|
+
|
|
|
|
|
+ def release(self):
|
|
|
|
|
+ self.sm.active(0)
|
|
|
|
|
+ if self.sm_id not in self.__class__._available_ids:
|
|
|
|
|
+ self.__class__._available_ids.append(self.sm_id)
|
|
|
|
|
+ self.__class__._available_ids.sort()
|