Kamuycikap - SentenceDataBase

日々の勉強の記録を気分で書き綴るブログ

Raspberry Pi3でCommon LispによるI2C通信

WiringPiライブラリをCommonLispから実行する

こう言うのをバインディングと呼ぶらしいです。
CommonLispにはCFFIと呼ばれる共通の仕組みが用意されていて、C言語で作成されたライブラリを利用できる仕組みが実現されています。

作成した回路

I2Cデバイスとして採用したのは、AdafruitのHT16K33を利用したLEDマトリクスです。
f:id:kamuycikap:20181028012659p:plain

WiringPiのCommonLisp

以下のkpri2c.lispとkmylogo.lispを作成し、kmylogo.lispを実行します。
実行環境は、ClozureCLで検証済み。

無事に実行が完了すると、実行ファイル「kmylogo」ができます。

kmylogo.lispのソースコメントにもありますが、ccl:save-application関数を全てコメントアウトすると、実行ファイルを作りません。
ccl:save-application関数直前にあるmain関数を実行することで、REPL上での実行となります。

kpri2c.lisp
;;;; I2C SPI Lib
(ql:quickload "cffi")


;; パッケージの作成
(defpackage :kpr-i2c
  (:use :common-lisp
        :common-lisp-user
        :cffi)
  (:export :wiring-pi-i2c-setup-interface
           :wiring-pi-i2c-setup
           :wiring-pi-i2c-read
           :wiring-pi-i2c-read8
           :wiring-pi-i2c-read16
           :wiring-pi-i2c-write
           :wiring-pi-i2c-write8
           :wiring-pi-i2c-write16))

(in-package :kpr-i2c)

;; wiringPiのバインディング
(define-foreign-library libwiringPi
  (:unix "libwiringPi.so"))

(use-foreign-library libwiringPi)

;;; C言語ヘッダファイル定義
;; #ifdef __cplusplus
;; extern "C" {
;; #endif

;; extern int wiringPiI2CSetupInterface (const char *device, int devId) ;
;; extern int wiringPiI2CSetup          (const int devId) ;

;; extern int wiringPiI2CRead           (int fd) ;
;; extern int wiringPiI2CReadReg8       (int fd, int reg) ;
;; extern int wiringPiI2CReadReg16      (int fd, int reg) ;

;; extern int wiringPiI2CWrite          (int fd, int data) ;
;; extern int wiringPiI2CWriteReg8      (int fd, int reg, int data) ;
;; extern int wiringPiI2CWriteReg16     (int fd, int reg, int data) ;


;; #ifdef __cplusplus
;; }
;; #endif


;; Core wiringPi functions I2C
(defcfun ("wiringPiI2CSetupInterface" wiring-pi-i2c-setup-interface) :int (device :pointer) (devId :int))
(defcfun ("wiringPiI2CSetup" wiring-pi-i2c-setup) :int (devId :int))

(defcfun ("wiringPiI2CRead" wiring-pi-i2c-read) :int (fd :int))
(defcfun ("wiringPiI2CReadReg8" wiring-pi-i2c-read8) :int (fd :int) (reg :int))
(defcfun ("wiringPiI2CReadReg16" wiring-pi-i2c-read16) :int (fd :int) (reg :int))

(defcfun ("wiringPiI2CWrite" wiring-pi-i2c-write) :int (fd :int) (data :int))
(defcfun ("wiringPiI2CWriteReg8" wiring-pi-i2c-write8) :int (fd :int) (reg :int) (data :int))
(defcfun ("wiringPiI2CWriteReg16" wiring-pi-i2c-write16) :int (fd :int) (reg :int) (data :int))
kmylogo.lisp
;;;;; I2C TEST (HT16K33)

;; ■HT16K33 DataSheet
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|
;; | Name                         | Address | Command(b0 ~ b7) | Option              | Description                                                 |
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|
;; | Display data Address pointer |    0x00 | A3 A2 A1 A0      | {A0~A3} R/W         | Five bits of immediate data, bits A0 to A3,                 |
;; |                              |         |                  |                     | are transferred to the data pointer to define               |
;; |                              |         |                  |                     | one of sixteen display RAM addresses.                       |
;; |                              |         |                  |                     |                                                             |
;; |                              |         |                  |                     | If the Display data register address (An)                   |
;; |                              |         |                  |                     | is 0X00h ~ 0X0Fh, after reaching the                        |
;; |                              |         |                  |                     | memory location 0X0Fh, the pointer will                     |
;; |                              |         |                  |                     | reset to 0X00h                                              |
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|
;; | System setup                 |    0x20 | X  X  X  S       | {S} Write           | Defines internal system oscillator on/off                   |
;; |                              |         |                  |                     | {0}:Turn off System oscillator (standby mode)               |
;; |                              |         |                  |                     | {1}:Turn on System oscillator (normaloperation mode)        |
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|
;; | Key data Address pointer     |    0x40 | 0 K2 K1 K0       | {K0~K2} Read        | Three bits of immediate data, bits K0 to                    |
;; |                              |         |                  |                     | K2, are transferred to the data pointer to                  |
;; |                              |         |                  |                     | define one of six key data RAM addresses.                   |
;; |                              |         |                  |                     | It is strongly recommended that the key                     |
;; |                              |         |                  |                     | data RAM of address 0x40H~0x45H                             |
;; |                              |         |                  |                     | should be read continuously and in one                      |
;; |                              |         |                  |                     | operation, so the key data RAM of address                   |
;; |                              |         |                  |                     | should be started at 0x40H only.                            |
;; |                              |         |                  |                     |                                                             |
;; |                              |         |                  |                     | If the Key data register address (An) is                    |
;; |                              |         |                  |                     | 0X40h ~ 0X45h, after reaching the memory                    |
;; |                              |         |                  |                     | location 0X45h, the pointer will reset to                   |
;; |                              |         |                  |                     | 0X40h                                                       |
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|
;; | INT flag Address pointer     |    0x60 | X X X X          | Read Only           | Defines the INT flag address, Read INT flag                 |
;; |                              |         |                  |                     | status.                                                     |
;; |                              |         |                  |                     | Interrupt flag signal output. When any key                  |
;; |                              |         |                  |                     |                                                             |
;; |                              |         |                  |                     | matrix key is pressed, after the completion of              |
;; |                              |         |                  |                     | two key scan cycles, this int flag bit goes to a            |
;; |                              |         |                  |                     | high level and remains at a high level until all            |
;; |                              |         |                  |                     | key data has been read                                      |
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|
;; | Display setup                |    0x80 | X B1 B0 D        | {D} Write           | {D} -> Defines Display on/off status                        |
;; |                              |         |                  |                     | 0: Display off                                              |
;; |                              |         |                  |                     | 1: Display on                                               |
;; |                              |         |                  |                     |                                                             |
;; |                              |         |                  | {B0~B1} Write       | Defines the blinking frequency                              |
;; |                              |         |                  |                     | {B1,B0} <- bit pattern                                      |
;; |                              |         |                  |                     | {0,0} -> Blinking OFF                                       |
;; |                              |         |                  |                     | {0,1} -> 2Hz                                                |
;; |                              |         |                  |                     | {1,1} -> 1Hz                                                |
;; |                              |         |                  |                     | {1,1} -> 0.5Hz                                              |
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|
;; | ROW/INT set                  |    0xA0 | X X Act Row/Int  | {Act Row/Int} Write | Defines INT/ROW output pin select and INT                   |
;; |                              |         |                  |                     | pin output active level status.                             |
;; |                              |         |                  |                     | {X 0}: INT/ROW output pin is set to ROW driver output       |
;; |                              |         |                  |                     | {0, 1}: INT/ROW output pin is set to INT output, activelow  |
;; |                              |         |                  |                     | {1, 1}: INT/ROW output pin is set to INT output, activehigh |
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|
;; | Dimming set                  |    0xE0 | P3 P2 P1 P0      | {P0~P3} Write       | Defines the pulse width of ROW.                             |
;; |                              |         |                  |                     | {P0~P3}: 0x00~0x0F                                          |
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|
;; | Test mode                    |    0xD9 | 1 0 0 1          | Write               | HOLTEK Use Only                                             |
;; |------------------------------+---------+------------------+---------------------+-------------------------------------------------------------|

(ql:quickload :cl-wiringpi2)
(load "kpri2c.lisp")

;; パッケージの作成
(defpackage :kpr-matrix
  (:use :common-lisp
        :common-lisp-user
        :cffi
        :kpr-i2c
        :wpi2)
;;  (:export :main))
  )

(in-package :kpr-matrix)

;; HT16K33 Command
(defconstant +i2c-address+ #x70)                  ; 対象のI2Cデバイスを選択
(defconstant +i2c-cms-data+ #x00)
(defconstant +i2c-cmd-sysset+ #x20)
(defconstant +i2c-cmd-blink+ #x80)
(defconstant +i2c-cmd-dimming+ #xE0)
(defconstant +i2c-cmd-outputsel+ #xA0)

;; HT16K33 RegSetup
(defconstant +i2c-sysset-osc-on+ #x01)

;; Display setup
(defconstant +i2c-blink-off+ #x00)
(defconstant +i2c-blink-display-on+ #x01)
(defconstant +i2c-blink-2hz+ #x02)
(defconstant +i2c-blink-1hz+ #x04)
(defconstant +i2c-blink-05hz+ #x06)

;; LED Data
(defparameter *char-pattern* nil)


(defparameter *toufu* '((#x00 #b11111111)                  ; 四角い枠。右上角から左下角に斜めの斜線
                        (#x02 #b10000011)
                        (#x04 #b10000101)
                        (#x06 #b10001001)
                        (#x08 #b10010001)
                        (#x0A #b10100001)
                        (#x0C #b11000001)
                        (#x0E #b11111111)
                        ))

(defparameter *invader1* '((#x00 #b00100100)                  ; インベーダーパターン1
                           (#x02 #b10100101)
                           (#x04 #b11111111)
                           (#x06 #b11011011)
                           (#x08 #b11111111)
                           (#x0A #b01111110)
                           (#x0C #b00100100)
                           (#x0E #b01000010)
                           ))


(defparameter *invader2* '((#x00 #b00100100)                  ; インベーダーパターン2
                           (#x02 #b00100100)
                           (#x04 #b01111110)
                           (#x06 #b11011011)
                           (#x08 #b11111111)
                           (#x0A #b11111111)
                           (#x0C #b10100101)
                           (#x0E #b00100100)
                           ))


(defparameter *squid1* '((#x00 #b00011000)                  ; 烏賊パターン1
                         (#x02 #b00111100)
                         (#x04 #b01111110)
                         (#x06 #b11011011)
                         (#x08 #b11111111)
                         (#x0A #b00100100)
                         (#x0C #b01011010)
                         (#x0E #b01000010)
                         ))


(defparameter *squid2* '((#x00 #b00011000)                  ; 烏賊パターン1
                         (#x02 #b00111100)
                         (#x04 #b01111110)
                         (#x06 #b11011011)
                         (#x08 #b11111111)
                         (#x0A #b00100100)
                         (#x0C #b01011010)
                         (#x0E #b10100101)
                         ))




(defun ht-cmd(fd cmd data)
    (wiring-pi-i2c-write fd (logior cmd data)))


;; HT16K33 Setup
(defun ht_init(fd)
  
  ;; HT16K33 初期値
  (ht-cmd fd +i2c-cmd-sysset+ #x01)              ; システムオシレータON
  (ht-cmd fd +i2c-cmd-blink+ #x01)               ; 点滅周期の設定
  (ht-cmd fd +i2c-cmd-dimming+ #x01)             ; LEDの明るさ設定
  )

;; LED Clear
(defun ht_clear(fd)
    (wiring-pi-i2c-write8 fd #x00 #x00)
    (wiring-pi-i2c-write8 fd #x02 #x00)
    (wiring-pi-i2c-write8 fd #x04 #x00)
    (wiring-pi-i2c-write8 fd #x06 #x00)
    (wiring-pi-i2c-write8 fd #x08 #x00)
    (wiring-pi-i2c-write8 fd #x0A #x00)
    (wiring-pi-i2c-write8 fd #x0C #x00)
    (wiring-pi-i2c-write8 fd #x0E #x00)
)

;; ビット反転(ネットから取ってきた)
(defun bit-reverse (n &optional (nbits (integer-length n)))
  (dotimes (i (ash nbits -1))
     (rotatef (ldb (byte 1 i) n)
              (ldb (byte 1 (- nbits i 1)) n)))
  n)

;; データをまとめて送信(list)
(defun ht_dataset_matrix(fd data-list)
    (mapcar #'(lambda(send-data)
                (ht_dataset fd (car send-data) (car (cdr send-data)))
                )
            data-list)
    )

;; HT16K33データ送信
    ;; A:1010 B:1011 C:1100 D:1101
    ;; Adafruitの8x8LEDマトリクスmini基板は、サイズを小さくするため配線が特殊になっている。
    ;; b0〜b7ビットのうち、b0〜b7を反転させなければならない。
    ;1111 1111 -> 1111 1111
    ;1011 1111 -> 1111 1110 : #xBF -> #xFE
    ;1101 1111 -> 1111 1101 : #xDF -> #xFD
(defun ht_dataset(fd line-add set-data)
  (let (
        b7                              ; 最上位ビット
        b0-b6                           ; b0〜b6
        )
    
;    (format t "INデータ:~8,b" set-data)
;    (princ #\Newline)
;    (format t "INデータHEX:~4,'0x" set-data)
;    (princ #\Newline)

    ;; 最上位ビットの取得
    (setf b7 (logand set-data #x80))    ; #b1000 0000 でマスクする。

    ;; b0〜b6ビットの取得
    (setf b0-b6 (logand set-data #x7F)) ; #b0111 1111 でマスクする。
    
;    (format t "最上位ビット:~b" b7)
;    (princ #\Newline)
;    (format t "リバース前:~8,'0b" b0-b6)
;    (princ #\Newline)

    ;; b0〜b6ビットの反転
;    (format t "リバース後:~8,'0b" (bit-reverse b0-b6 7))
;    (princ #\Newline)

;    (format t "書き込みデータ:~b" (logior b7 (bit-reverse b0-b6 7)))
;    (princ #\Newline)
;    (format t "HEX:~2,'0x" (logior b7 (bit-reverse b0-b6 7)))
;    (princ #\Newline)

    ;; HT16K33へデータ書き込み
    (wiring-pi-i2c-write8 fd line-add (logior b7 (bit-reverse b0-b6 7)))
  ))

(defparameter *dbg-fd* nil)                ; Debug用
(defparameter *fd* nil)
(defparameter *dbg* nil)

(defun main()
  (let (
        (fd)
        )

    ;; I2Cシステムの初期化
    (setf fd (wiring-pi-i2c-setup +i2c-address+))
    (setf *fd* fd)
    
    ;; HT16K33 Setup
    (ht_init fd)

    ;; LED All OFF
    (ht_clear fd)

    ;; Send Data
    (setf *char-pattern* (cons *invader2* *char-pattern*))
    (setf *char-pattern* (cons *invader1* *char-pattern*))
    (setf *char-pattern* (cons *invader2* *char-pattern*))
    (setf *char-pattern* (cons *invader1* *char-pattern*))
    (setf *char-pattern* (cons *squid2* *char-pattern*))
    (setf *char-pattern* (cons *squid1* *char-pattern*))
    (setf *char-pattern* (cons *squid2* *char-pattern*))
    (setf *char-pattern* (cons *squid1* *char-pattern*))


    (loop
    (mapcar #'(lambda(led-char)
                (ht_dataset_matrix fd led-char)
                (delay 700)
                )
            *char-pattern*)
       )



    ))

;; このコメントを外せばREPL上で実行
;;(main)

;; main関数を起点として実行ファイルを作成
(ccl:save-application "kmylogo"
                      :toplevel-function #'main        ; トップレベルをmainに
                      :prepend-kernel t)