OpenHSP icon indicating copy to clipboard operation
OpenHSP copied to clipboard

SPI で全二重通信をするためのインターフェースを追加

Open RollMan opened this issue 1 year ago • 0 comments

SPI で全二重通信をするためのインターフェースを追加しました。 SPI による全二重通信をリクエストする関数 (SPI_Transceive) と、リクエストに付与するパラメータを設定する関数 (SPI_Configure{H,M,L})、加えて SPI ドライバのパラメータを 変更する関数

  • SPI_{RD,WR}_MODE
  • SPI_{RD,WR}_MODE32
  • SPI_{RD,WR}_LSB_FIRST
  • SPI_{RD,WR}_BITS_PER_WORD
  • SPI_{WD,WR}_MAX_SPEED_HZ

を追加しました。これに伴い、 MCP3008 の専用実装を削除しました。

詳細

これまでの OpenHSP の SPI サポートは read/write システムコールによるものに限られており、 したがって半二重通信のみがサポートされていました (read/write では全二重通信はできない [1])。 しかし SPI はデータ線が 2 本あり、 いくつかの SPI デバイスは全二重通信による通信を要求します。 その場合は HSP ランタイムに専用実装を埋め込んでいる 状況でした。

たとえば Microchip 社の ADC である MCP3008 [2] は 3 bytes 単位で通信を行いデータをやり取りしますが、 マスターは 2 byte 目でデバイスに対しコマンドを送信しつつ 2 byte 目の 7 bit 目からデータの受信を 開始する必要があります。 これは厳密には全二重通信とは言わないかもしれませんが、 read/write システムコールは 8 bit 単位での 半二重通信を行うため 2 byte 目を write() している間は 7, 8 bit 目を read() することができません。 完全なデータのやり取りをするには IOCTL による 全二重通信が必要になります。

この commit は HSP スクリプトからアクセスできる汎用的で 全二重通信 (IOCTL 経由) 可能な SPI インターフェースを 追加します。また IOCTL で全二重通信 (SPI_IOC_MESSAGE(N)) をリクエストするときに必要なパラメータ struct spi_ioc_transfer を設定する関数も追加しました。 この構造体に設定すべきパラメータは 7 個ある一方、 devcontrol は引数を 3 個までしか受け付けないようなので 関数は 3 個に分けて実装しました (SPI_Configure_H for higher 3 fields, SPI_Configure_M for middle 3 fields,, SPI_Configure_L for the last field)。 最後に、転送モード等のSPI ドライバの設定 [1] を 変更するための関数も追加しました。 特に転送モードは SPI_IOC_MESSAGE(N) のパラメータで、 temporarily に変更できないため必要です。

これに伴い、ランタイムに含まれていたデバイスごとの 専用実装を削除しました。

動作確認

Raspberry Pi OS Bookworm が動作する Raspberry Pi 400 にて hsp3dish, hsp3cl 両方のランタイムで MCP3008 と 通信できることを確認しました。

https://github.com/RollMan/hsp_spi_pr_test_by_mcp3008/commit/a0cfab4bcd83e6310fbdc894e6889748656e70fa

; Configure spidev options
devcontrol "spisetmaxspeedhz", 10000, ch
devcontrol "spisetmode", 0, ch
devcontrol "spisetlsbfirst", 0, ch
devcontrol "spisetbitsperword", 8, ch
devcontrol "spiconfigureh", 10000, 0, 8
devcontrol "spiconfigurem", 1, 0, 0	; Keep selecting the device for the 3-bytes command
devcontrol "spiconfigurel", 0

; Start communication
;; Prepare a buffer as {dont care, upper 2 bits of data, lower 8 bits of data}
dim recv, 3
;; Send start byte
devcontrol "spitransceive", 1, 1, ch
byte(0) = stat
;; Send control byte: SINGLE_ENDED (0x80) | adc_pin.
;; In bit-wire sepresentation, {single/diff, adc_pin[2], adc_pin[1], adc_pin[0], x, x, x}
SIGNLE_ENDED = 0x80
devcontrol "spitransceive", SINGLE_ENDED | (adc_pin << 4), 1, ch
byte(1) = stat
;; Send an empty byte (avoiding start byte 0x01) and receive rest data.
devcontrol "spiconfigurem", 0, 0, 0	; Deselect the device
devcontrol "spitransceive", 0x00, 1, ch
byte(2) = stat
res = ((0x03 & byte(1)) << 8) | byte(2)
mes res

参考

  • [1] https://www.kernel.org/doc/Documentation/spi/spidev
  • [2] https://www.microchip.com/en-us/product/mcp3008
  • https://codebrowser.dev/linux/linux/include/uapi/linux/spi/spi.h.html
  • https://codebrowser.dev/linux/linux/include/uapi/linux/spi/spidev.h.html

RollMan avatar Oct 12 '24 00:10 RollMan