nRF Connect SDK:SPIM(本物)

# SDK 2.xで一度書いた
SPIMについてはSDK 2.xになったときに一度記事を書いています。

https://qiita.com/yoshihiro0709/items/afe5316372b80f0a3d4a

が、この時に書いたものはまだNRFXのAPIを直接叩いています。つい最近、NRFXのAPIを使わない形のTWIMを記事にしましたが、SPIMも同様にNRFXのAPIを直接叩かない形(HAL)があるはずだよね・・・ということで調べてみました。

::: note
TWIMの場合はAPIを直接叩くプログラムが動かなかったので仕方がなく・・・。
:::

# SPIMも同様にサンプルがある
TWIMと同じようにSPIMにもサンプルがあります。しかも同じ富士通のFRAM(笑)です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/de09a72a-4fa1-6601-f9a5-37f46a39f4ff.png)
## 説明は省略
サンプルプロジェクトそのものの説明は省略します。読みたい人は読んでください。
ところで今頃気が付きましたが、このサンプルプロジェクトってCSを制御していないですね。気になったのでICの仕様を調べてみましたがちゃんとCSピンはあるみたいです。

::: note
当たり前か・・・
:::

# 例によって
私のサンプルプロジェクトではInvensenseのICM-2064xを動かします。これは手元にあってお手軽に扱える評価ボードがこれしかないからという理由によるものです。ですので、ソースコード中にはIC固有の情報表記が多々出てきますが、そこらへんは無視するか自分で調べてください。
## まずはペリフェラルの設定
何度も出てきていますが、評価ボード(正確には評価ボード用に用意されたDeviceTree)を使うとペリフェラルの設定が固定されてしまっているためOverlayを使って設定を変えてあげる必要があります。今回はSPIM0を使いたいのでTWIM0をdisableにした上でSPIM0を有効にしています。

``` app.overlay
// Copyright (c) 2022 Nordic Semiconductor ASA
// SPDX-License-Identifier: Apache-2.0

&i2c0 {
	status = "disabled";
};

&i2c1 {
	status = "disabled";
};

&spi0 {
	compatible = "nordic,nrf-spim";
	status = "okay";
	cs-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>;
};
```

ちなみにSPIM0が使うピンはそれぞれ
```
SCLK = P0.27
MOSI = P0.26
MISO = P0.29
```
となっています。そこに加えてapp.overlayでCSをP0.28として定義しています。
# ソースコード
TWIMでも似たようなコードを書いているのでいきなり載せてしまいます。ただ、TWIMと違うのはCSピンを使うように設定してやらないといけないという点です。CSピンを使うコンフィグをSPIM Read/Writeを呼び出すたびに渡しているので、CS以外のピンがパラレル接続になっている場合にコントロールするCSを変えながら動かすというのも簡単にできそうです。

::: note
僕はそもそもそんなハードウェア作りませんけどね・・・めんどくさいから(笑)
:::

``` main.c
/*
 * Copyright (c) 2012-2014 Wind River Systems, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/spi.h>
#include "defines_ICM2064x.h"

const uint8_t INIT_CMD[] = {
    UB0_PWR_MGMT_1,
    WAIT_MSEC,
    UB0_USER_CTRL,
    UB0_INT_PIN_CFG,
    UB0_PWR_MGMT_1,
    UB0_LP_CONFIG,
    UB0_REG_BANK_SEL,
    UB2_GYRO_SMPLRT_DIV,
    UB2_GYRO_CONFIG_1,
    UB2_GYRO_CONFIG_2,
    UB2_ACCEL_SMPLRT_DIV_1,
    UB2_ACCEL_SMPLRT_DIV_2,
    UB2_ACCEL_CONFIG,
    UB2_REG_BANK_SEL,
    UB0_PWR_MGMT_1,
};
const uint8_t INIT_PARAM[] = {
    PWR_MGMT_1_DEVICE_RESET,
    0x64,
    0x00,
    INT_PIN_CFG_BYPASS_EN,
    PWR_MGMT_1_CLKSEL_AUTO,
    LP_CONFIG_ACCEL_CYCLE | LP_CONFIG_GYRO_CYCLE,
    REG_BANK_SEL_USER_BANK_2,
    0x04,
    GYRO_CONFIG_1_GYRO_DLPFCFG_0 | GYRO_CONFIG_1_GYRO_FS_SEL_FULL | GYRO_CONFIG_1_GYRO_FS_SEL_GYRO_FCHOICE,
    0x00,
    0x00,
    0x04,
    ACCEL_CONFIG_ACCEL_FS_SEL_FULL,
    REG_BANK_SEL_USER_BANK_0,
    PWR_MGMT_1_LP_EN | PWR_MGMT_1_CLKSEL_AUTO,
};

/* write */
static int write_bytes(const struct device *spi, struct spi_config *spi_cfg, uint8_t *data, uint32_t num_bytes)
{
	int err;
	struct spi_buf bufs[] = {
		{
			.buf = data,
			.len = num_bytes
		}
	};
	struct spi_buf_set tx = {
		.buffers = bufs,
		.count = 1
	};
	err = spi_write(spi, spi_cfg, &tx);

	if (err) {
		printk("Error during SPI write\n");
		return -EIO;
	}

	return 0;
}

/* read */
static int read_bytes(const struct device *spi, struct spi_config *spi_cfg, uint8_t *tx_data, uint32_t tx_num_bytes, uint8_t *rx_data, uint32_t rx_num_bytes)
{
	int err;
	struct spi_buf bufs_tx[] = {
		{
			.buf = tx_data,
			.len = tx_num_bytes
		}
	};
	struct spi_buf bufs_rx[] = {
		{
			.buf = rx_data,
			.len = rx_num_bytes
		}
	};
	struct spi_buf_set tx = {
		.buffers = bufs_tx,
        .count = 1
	};
    struct spi_buf_set rx = {
        .buffers = bufs_rx,
        .count = 1
    };

    err = spi_transceive(spi, spi_cfg, &tx, &rx);
	if (err) {
		printk("Error during SPI read\n");
		return -EIO;
	}

	return 0;
}

void main(void)
{
	const struct device *spi;
	static const struct spi_cs_control spi_dev_cs_ctrl = {
		.gpio = GPIO_DT_SPEC_GET(DT_NODELABEL(spi0), cs_gpios),
	};
	struct spi_config spi_cfg = {
		.operation = SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8),
		.frequency = 4000000U,
    	.cs = &spi_dev_cs_ctrl,
    	.slave = 0,
	};
	int err;
	uint8_t tx_buf[16] = {0};
	uint8_t rx_buf[16] = {0};

	spi = DEVICE_DT_GET(DT_NODELABEL(spi0));
	if (!device_is_ready(spi)) {
		printk("SPI device %s is not ready\n", spi->name);
		return;
	}

	for (int i = 0; i < sizeof(INIT_CMD); i++) {
		if (INIT_CMD[i] == WAIT_MSEC) {
			// Wait
			k_sleep(K_MSEC(INIT_PARAM[i]));
		} else {
			// Transfer
			tx_buf[0] = INIT_CMD[i];
			tx_buf[1] = INIT_PARAM[i];
			// write
			err = write_bytes(spi, &spi_cfg, tx_buf, 2);
			if (err) {
				printk("Error writing to device! error code (%d)\n", err);
				return;
			}
		}
	}

	tx_buf[0] = (UB0_ACCEL_XOUT_H | 0x80) & 0xfe;
	tx_buf[1] = 0x00;
	err = read_bytes(spi, &spi_cfg, tx_buf, 2, rx_buf, 14);
	if (err) {
		printk("Error reading from device! error code (%d)\n", err);
		return;
	}
}
```

リードもちゃんとできています。
![SPIM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/340602/b2a32c39-d9d3-aafe-4b0d-963fa9121d4d.png)