CommonLisp에서 RaspberryPi 전자 공작 ~I2C 온도 센서~

소개



이번에는 I2C에서 ADT7410을 사용한 온도 센서 모듈을 제어하고 싶습니다.

회로도



이번에는 먼저 RaspberryPi와 ADT7410을 먼저 연결해 버립니다.



기기 주소 확인



그런 다음 MI2CLCD-01의 디바이스 주소를 확인합니다.
장치 주소를 확인하려면 i2cdetect 명령을 사용합니다.
pi@raspberrypi:~ $ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

패키징



cffi를 Quicklisp로로드하고 패키지 정의.
평소입니다.

packages.lisp
;; cffiをQuicklispでロード
(ql:quickload "cffi")

;; cl-cffiパッケージを定義
(defpackage :cl-cffi
    (:use :common-lisp :cffi))

API 래퍼 작성



이번에 추가할 함수는 다음 중 하나뿐입니다.

· wiringPiI2CReadReg16
표시된 디바이스 레지스터에서 16비트 값을 읽습니다.

libwiringPi.lisp
(define-foreign-library libwiringPi
  (:unix "libwiringPi.so"))

(use-foreign-library libwiringPi)

;; Initialization of the wiringPi
(defcfun "wiringPiSetupGpio" :int)

;; Set the mode of the GPIO pin
(defcfun "pinMode" :void (pin :int) (mode :int))

;; GPIO pin output control
(defcfun "digitalWrite" :void (pin :int) (value :int))

;; Waiting process
(defcfun "delay" :void (howlong :uint))

;; Set the state when nothing is connected to the terminal
(defcfun "pullUpDnControl" :void (pin :int) (pud :int))

;; Read the status of the GPIO pin
(defcfun "digitalRead" :int (pin :int))

;; Initialization of the I2C systems.
(defcfun "wiringPiI2CSetup" :int (fd :int))

;; Writes 8-bit data to the instructed device register.
(defcfun "wiringPiI2CWriteReg8" :int (fd :int) (reg :int) (data :int))

;; It reads the 16-bit value from the indicated device register.
(defcfun "wiringPiI2CReadReg16" :int (fd :int) (reg :int))

I2C ADT7410 프로그램 본체



ADT7410에서 온도 데이터를 가져와 콘솔에 표시하는 프로그램입니다.
해상도 16 비트의 고정밀도로 온도를 얻기 위해 레지스터 0x03에 "0x80"을 쓰고 있습니다.
또, 온도 데이터를 취득하면 빅 엔디안이 되어 버리고 있으므로, 바이트 스왑 해 리틀 엔디안으로 할 필요가 있습니다.

【온도 계산에 대해】
・해상도 13비트의 경우
4~16비트째까지가 유효한 데이터이므로 취득 데이터를 8로 나누어 하위 3비트를 버린 다음 온도 분해능값인 0.0625를 곱합니다.
계산식:(취득 데이터/8) × 0.0625

・해상도 16비트의 경우
모든 데이터를 사용할 수 있으므로 그대로 온도 분해능치인 0.0078이라고 합니다.
계산식: 취득 데이터 × 0.0078

adt7410.lisp
;; Load packages
(load "packages.lisp" :external-format :utf-8)

(in-package :cl-cffi)

;; Load wrapper API
(load "libwiringPi.lisp" :external-format :utf-8)

;; I2C device address (0x48)
(defconstant +i2c-addr+ #X48)

;; バイトスワップ関数
(defun byte-swap (num-value)
  (let (str-value temp-msb temp-lsb)
    ;; 数値を文字列へ変換
    (setq str-value (write-to-string num-value :base 16))
    ;; 上位2桁(MSB)を取得
    (setq temp-msb (subseq str-value 0 2))
    ;; 下位2桁(LSB)を取得
    (setq temp-lsb (subseq str-value 2))
    ;; スワップして結合
    (setq str-value (concatenate 'string temp-lsb temp-msb))
    ;; 文字列を数値へ変換
    (parse-integer str-value :radix 16)))

;; メイン関数
(defun main ()
  (let (fd base-data actual-data result)
    ;; i2cの初期設定
    (setq fd (wiringPiI2CSetup +i2c-addr+))
    ;; 温度を16ビットのデータで取得するようレジスタ「0x03」に設定
    (wiringPiI2CWriteReg8 fd #X03 #X80)
    ;; ADT7410からデータを取得
    (setq base-data (wiringPiI2CReadReg16 fd #X00))
    ;; バイトスワップ
    (setq actual-data (byte-swap base-data))
    ;; 温度計算
    (setq result (* actual-data 0.0078))
    ;; 結果を標準出力
    (format t "~d~%" result)))

;; 実行!
(main)

실행



다음 명령으로 실행합니다.
sbcl --load adt7410.lisp

이하 실행중인 모습





측정: 28.421875°C
알람 온도계: 28.7°C

꽤 가까운 결과가 나오네요.

마지막으로



마침내 센서에서 데이터를 검색하는 곳까지 왔습니다.
역시 센서는 어렵네요.

우선, 이번 제일 힘들었던 것은 바이트 스왑 처리입니다・・・.

좋은 웹페이지 즐겨찾기