uLisp 點亮 TM1637 四位元七段顯示器

點亮一個四位數的七段顯示器很複雜也很簡單,簡單的是直接用 Arduino 的範例去做就好,複雜的是自己想要自己生一個 lisp 驅動的程式…

1. 目標

建立 Arduino UNO 使用 uLisp 驅動的四位數七段顯示器的 API 。

2. TM1637 IC 介紹

TM1637 是一種帶鍵盤掃描接口的 LED(發光二極管顯示器)驅動控制專用電路,內部集成了 MCU 數字接口、數據鎖存、LED 高壓驅動和鍵盤掃描。 由於 TM1637 的傳輸介面並非正規的 \(I^2C\) ,所以要特別建立與 uLisp 溝通的方式。 TM1637 Datasheet

3. 使用介面

自製 common lisp 與 Arduino UNO 開發板連接的程式, cl-connect-arduino 。

4. TODO API 製作

4.1. DONE cl-connect-arduino 與 Arduino UNO 連接

4.2. DONE 確認電子模組與 Arduino UNO 連接方式

模組 Arduino UNO
CLK Pin 2
DIO Pin 3
VCC 5V
GND GND

5. common lisp

5.1. ulisp-led-4-bit-display.asd

  ;;;;-*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
(in-package :cl-user)

(asdf:defsystem #:ulisp-led-4-bit-display
  :description "uLisp control 4-bit display"
  :author "Colin Lu"
  :License "GPL"
  :version "0.0.1"
  :depends-on ("cl-connect-arduino")
  :serial t
  :components ((:file "packages")
               (:file "ulisp-led-4-bit-display")))

5.2. packages.lisp

(in-package :cl-user)

(defpackage #:ulisp-led-4-bit-display
  (:use #:cl #:cl-connect-arduino))

5.3. ulisp-led-4-bit-display.lisp

(in-package :ulisp-led-4-bit-display)

(defmacro run-command-arduino (&body body)
  (let ((commands (gensym))
        (serial-name (gensym)))
    `(let ((,commands '(,@body))
           (,serial-name ,*arduino-serial*))
       (dolist (x ,commands)
         (arduino-command ,serial-name (apply #'list x))))))


(defun tm1637-set ()
  (run-command-arduino
    (defvar *scl* 2)
    (defvar *sda* 3)
    (defvar *delaytime* 5)

    (defun tm1637 ()
      (pinmode *scl* :output)
      (pinmode *sda* :output)
      (digitalwrite *scl* :high)
      (digitalwrite *sda* :high))

    ;;定義腳位狀況
    (tm1637) 

    (defun start ()
      (digitalwrite *scl* :high)
      (digitalwrite *sda* :high)
      (digitalwrite *sda* :low)
      (bitdelay)
      (digitalwrite *scl* :low)
      (format t "~&start~%"))

    (defun stop ()
      (digitalwrite *scl* :low)
      (digitalwrite *sda* :low)
      (digitalwrite *scl* :high)
      (bitdelay)
      (digitalwrite *sda* :high)
      (format t "~&stop~%"))

    (defun re-start ()
      (stop)
      (start))

    (defun writebyte (data)
      (let ((ack t))
        ;;(format t "~&data: ~A~%" data)
        (dotimes (x 8)
          (let ((high-p (logbitp x data)))
            (digitalwrite *scl* :low)
            (if high-p
                (digitalwrite *sda* :high)
                (digitalwrite *sda* :low))
            ;; (format t "~A " high-p)
            (digitalwrite *scl* :high)))
        (digitalwrite *scl* :low)
        ;; wait for the ack
        (digitalwrite *sda* :high)
        (digitalwrite *scl* :high)
        (pinmode *sda* :input)
        ;; (format t "~&~A ~A" (digitalread *sda*) (digitalread *scl*))
        (bitdelay)
        ;;(format t "~&ack :~A~%" ack)
        (setf ack (digitalread *sda*))
        ;;(format t "~&ack :~A~%" ack)
        (unless ack
          (pinmode *sda* :output)
          (digitalwrite *sda* :low))

        (bitdelay)
        (digitalwrite *sda* :high)
        (bitdelay)
        ack))

    (defun bitdelay (&optional (times *delaytime*))
      (delay times))

    ;; (defun display (bit-addr disp-data)
    ;;   (let ((addr-fixed #x44)
    ;;      (addr-auto #x40))
    ;;  (start)
    ;;  (writebyte addr-fixed) ;; command1
    ;;  (stop)
    ;;  (start)
    ;;  (writebyte (or bit-addr #xc0)) ;;command2
    ;;  (writebyte disp-data)
    ;;  (stop)
    ;;  (start)
    ;;  (writebyte #x8f) ;; cmd_disp_ctrl, command3
    ;;  (stop)
    ;;  ))
    ))

(defun all-bright ()
  (run-command-arduino 
    (tm1637)
    (start)
    (writebyte #x40)
    (re-start)
    (writebyte #xc0)
    ;; (dotimes (x 6)
    ;;   ;; 手動設定
    ;;   ;;(writebyte #xff)
    ;;   (writebyte #xff)
    (dolist (x '(#x38 #x06 #x6d #x73 #xff #xff))
      (writebyte x))
    (re-start)
    (writebyte #x8f)
    (stop)))

5.4. 程式重點

  • 要注意 I2C format 位元讀取時間是在 scl 高位元才會讀取,sda 在 scl 低位元時變換才有效。
  • 在 scl 高位元變換 sda 訊號時,為 start 位元,及 stop 位元
  • 一定要等 ack 位元回傳

6. 參考