SPI ３軸加速度センサ(LIS3DH)と連携する

最初に見たティム・バートンの作品はシザー・ハンドだったと思う。ジョニー・ディップの怪演も魅力的であったが何よりストーリーが素晴らしかった。その後「The Nightmare Before Christmas」の DVD を購入するも、この時期に見ないと意味がない気がしてDVD はほこりをかぶったままだ。先生も走る時期にはなかなか DVD をゆっくり見る時間が取れない。


秋葉原で容易に入手可能な LIS3DH という３軸加速度センサと連携してみよう。SPI との連携はすでに書いている。

```python:spi_lis3dh.py
import polyphony
from polyphony.io import Port
from polyphony.typing import bit, uint3, uint12, uint24
from polyphony.timing import clksleep, clkfence, wait_rising, wait_falling


CONVST_PULSE_CYCLE = 10
CONVERSION_CYCLE = 39

@polyphony.module
class spi_lis3dh:
    def __init__(self):
        self.sclk = Port(bit, 'out')
        self.mosi = Port(bit, 'out')
        self.miso = Port(bit, 'in')
        self.cs_n = Port(bit, 'out', init=1)

        self.x_led = Port(bit, 'out')
        self.y_led = Port(bit, 'out')
        self.z_led = Port(bit, 'out')

        self.data24  = Port(uint24, 'out', protocol='ready_valid')
        self.append_worker(self.worker)

    def set_addr(self, rw, ms, addr):
        clksleep(1)
        self.sclk.wr(0)
        self.mosi.wr(rw)

        clksleep(2)
        self.sclk.wr(1)

        clksleep(2)
        self.sclk.wr(0)
        self.mosi.wr(ms)

        clksleep(2)
        self.sclk.wr(1)

        clksleep(2)
        self.sclk.wr(0)

        for i in range(6):
            bit1 = (addr >> (5 - i)) & 1
            self.mosi.wr(bit1)
            clksleep(1)
            self.sclk.wr(1)

            clksleep(1)
            self.sclk.wr(0)

        clksleep(1)

    def read_data(self, addr):
        self.cs_n.wr(0)
        self.sclk.wr(0)
        clksleep(1)

        self.set_addr(1, 0, addr)
        data = 0

        for i in range(8):
            data <<= 1
            clksleep(1)
            self.sclk.wr(1)

            clksleep(1)
            bit1 = self.miso.rd() & 1
            clkfence()
            self.sclk.wr(0)
            data |= bit1

        clksleep(1)
        self.cs_n.wr(1)
        return data

    def write_data(self, addr, data):
        self.cs_n.wr(0)
        self.sclk.wr(0)
        clksleep(1)

        self.set_addr(0, 0, addr)

        for i in range(8):
            bit1 = (data >> (7 - i)) & 1
            self.mosi.wr(bit1)
            clksleep(1)
            self.sclk.wr(1)

            clksleep(1)
            self.sclk.wr(0)

        clksleep(2)
        self.cs_n.wr(1)
        self.mosi.wr(0)
        return data

    def worker(self):
        self.cs_n.wr(1)
        self.sclk.wr(0)
        clksleep(1)

        self.write_data(0x20, 0x7F)
        while polyphony.is_worker_running():
            clksleep(20)

            self.write_data(0x20, 0x7F)
            clksleep(10)

            data_who_am_i = self.read_data(0x0F)
            clksleep(10)

            data_x_l = self.read_data(0x29)
            clksleep(10)

            data_y_l = self.read_data(0x2B)
            clksleep(10)

            data_z_l = self.read_data(0x2D)
            clksleep(10)

            self.x_led.wr(1 if data_x_l > 0x30 else 0)
            self.y_led.wr(1 if data_y_l > 0x30 else 0)
            self.z_led.wr(1 if data_z_l > 0x30 else 0)

            data_xyz = ( data_x_l << 16 ) | ( data_y_l << 8 ) | data_z_l
            self.data24.wr(data_xyz)

@polyphony.testbench
def test(spic):
    data = spic.data24.rd()
    print("data0:" , data)
    data = spic.data24.rd()
    print("data1:" , data)

if __name__ == '__main__':
    spic = spi_lis3dh()
    test(spic)
```

Arty Z7 などにつなぐことで加速度センサからの情報を見て LED を光らせることが出来る。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/99921/424c37df-c3f5-35d6-cb0d-e4f4ac3226dc.png)


肝となる関数は read_data と write_data そしてそこからよばれるset_addr であろう。SPI の sclk, mosi, miso, cs_n をコントロールしてLIS3DH とやりとりをする。

直接 SPI でやりとりしており CPU を介していないところが特徴だ。例えば加速度センサーを４つつけてもすべて(ほぼ)同じクロックで実行することが可能だろう。

SPI の信号線はタイミング制御があるのでソース上にはclksleep や clksleep が散在する結果となっている。この辺りはもう少し簡潔にかけるようにすべきなのだろう。