CommonLisp에서 RaspberryPi 전자 공작 ~시리얼 통신~

소개



이번에는 초소형 USB 시리얼 모듈【MPL2303SA】 을 사용하여 RaspberryPi와 PC에서 시리얼 통신을 해보고 싶습니다.

직렬 콘솔 비활성화



불행히도 Raspberry Pi 설정에서 시리얼을 활성화하는 것만으로는 직렬 포트를 외부 장치와 통신하는 데 사용할 수 없습니다.
왜냐하면 기본적으로 직렬 포트가 직렬 콘솔로 작동하기 때문입니다.
따라서 다음 명령으로 직렬 콘솔을 비활성화해야 합니다.

RaspberryPi3의 경우:
sudo systemctl stop [email protected]
sudo systemctl disable [email protected]

RaspberryPi3 이외의 경우:
sudo systemctl stop [email protected]
sudo systemctl disable [email protected]

그런 다음/boot/cmdline.txt의 "console=serial0, 115200"부분을 삭제하고 파일을 저장하고 RaspberryPi를 다시 시작합니다.
sudo vi /boot/cmdline.txt

RaspberryPi3과 다른 차이점은 직렬 포트에 사용되는 장치입니다.
RaspberryPi3에서는 Bluetooth를 사용할 수 있게 되어 있습니다만, 이 Bluetooth용의 디바이스로서 지금까지 시리얼 포트로서 사용되고 있던 디바이스가 할당되어 버렸기 때문에, 본래의 시리얼 포트의 디바이스는 변경되어 버렸습니다.

회로도



회로도는 다음과 같습니다.



패키징



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

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

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

API 래퍼 작성



이번에 추가하는 함수는 다음 3가지입니다.

・serialOpen
직렬 장치를 초기화하고 전송 속도를 설정합니다.

・serialClose
지정된 파일 기술자로 식별되는 디바이스를 닫습니다.

・serialPutchar
지정된 파일 기술자에 의해 식별되는 디바이스에 1 바이트를 송신합니다.

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

(use-foreign-library libwiringPi)

;;;; Core function

;; 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))

;;;; I2C Library

;; 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))

;;;; SPI library

;; Initialization of the SPI systems.
(defcfun "wiringPiSPISetup" :int (channel :int) (speed :int))

;; Execute concurrent write / read transactions on the selected SPI bus
(defcfun "wiringPiSPIDataRW" :int (channel :int) (data :pointer) (len :int))

;;;; Serial Library

;; シリアルデバイスを初期化し、ボーレートを設定します。
(defcfun "serialOpen" :int (device :string) (baud :int))

;; 指定されたファイル記述子によって識別されるデバイスを閉じます。
(defcfun "serialClose" :void (fd :int))

;; 指定されたファイル記述子によって識別されるデバイスに1バイトを送信します。
(defcfun "serialPutchar" :void (fd :int) (c :unsigned-char))

시리얼 통신 프로그램 본체



표준 입력한 문자열을 시리얼 통신으로 송신하는 프로그램입니다.

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

(in-package :cl-cffi)

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

(defun main ()
  (let (fd input-data)
    ;; 初期化(RaspberryPi3を使っている人は "/dev/ttyS0" を使ってください。)
    (setf fd (serialOpen "/dev/ttyAMA0" 115200))
    (if (< fd 0)
        (return-from main nil))

    ;; 1行分の入力された文字列を読み取る
    (setf input-data (read-line))

    ;; 文字列をシリアル通信で送信
    (loop :for char :across input-data
       :do (serialPutchar fd (char-code char)))

    ;; 改行コード(LF)を入れる
    (serialPutchar fd #X0A)

    ;; クローズ処理
    (serialClose fd)))

;; 実行!
(main)

Tera Term 설정



PC 측에서는 Tera Term을 사용하여 데이터를 수신합니다.

Tera Term을 시작하고 직렬 포트를 선택한 다음 확인을 클릭합니다.



메뉴 바에서 "설정 (S)"⇒ "시리얼 포트 (E) ..."를 선택하면 다음과 같은 화면이 나타납니다.
빨간색 테두리로 둘러싸인 부분을 "115200"으로 설정하고 "확인"을 클릭하십시오.



다음에, 메뉴 바에서 「설정(S)」⇒「단말(T)...」을 선택하면 이하와 같은 화면이 나타납니다.
빨간색 프레임으로 둘러싼 부분을 "LF"로 설정하고 "확인"을 클릭하십시오.



이것으로 PC 측 준비가 완료됩니다.

실행



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

RaspberryPi 측의 실행중인 모습



PC측의 실행중의 모습



제대로 PC 측에서 문자열을 수신할 수 있네요.
그리고, 개행되고 있는 것이 커서 위치로부터 알 수 있습니다.

마지막으로



「그렇지 않아, 시리얼 통신 아직 하지 않았구나~, 좋을까!」라고 가벼운 기분 처음 보았습니다만, 설정으로 마음껏 망설여 버렸습니다. (웃음)
설마 이렇게 귀찮게 되어 있었다고는 생각하지 않았던 것으로・・・.

이번 소스 코드는 Github에서 공개하고 있습니다.
잘하면 아래에서 부디 ~.
· Github

좋은 웹페이지 즐겨찾기