STM32でWiiのヌンチャクを使う

##はじめに
freeRTOSの勉強のため、STM32F103をArduinoでは無くSTM32CubeIDEを使用していろいろ動作させる。
今回はI2Cデバイスの操作として家に転がっていたWiiのヌンチャクを動作させた。

##使用した環境
- ハードウェア
 - マイコン　：STM32F103C8T6　(別名Black Pill)　300円ぐらい
 - デバッガ　：ST-LinkV2-1　([NUCLEO](https://www.st.com/en/evaluation-tools/nucleo-f446re.html)付属のものを使用)　1500円ぐらい
 - Wiiヌンチャク　(白、Wii本体の発売日に購入)
 - USBシリアル変換　([こんなの](http://akizukidenshi.com/catalog/g/gM-08461/))

- ソフトウェア
 - STM32CubeIDE　(ST社公式からダウンロード)
 - freeRTOS　　　(上記CubeIDEに内包されている)

##ハードウェアの準備
1. ヌンチャクとマイコンを接続するため、ヌンチャクのケーブルをバラし4本の線
 - 白：GND
 - 赤：3.3V
 - 緑：SDA
 - 黄：SCL
を出します。

1. I2C用のプルアップ抵抗の接続
　2本の抵抗(2~3KΩ程度)を使用し、SDA,SCLと3.3Vを抵抗でつなぐ。

1. マイコンとの接続
　4線を下記のように接続します。

|マイコン側|---|ヌンチャク側|
|:--:|:---:|:---:|
|GND|---|白：GND|
|V3|---|赤：3.3V|
|PB7|---|緑：SDA|
|PB6|---|黄：SCL|
　(SDA,SCLはプルアップ抵抗経由で3.3Vにも接続されています)

## I2C仕様
arduinoのライブラリなどから以下のことが分かった。
### 基本情報
 - デバイスID　：　0x52@7bit
 - 初期化　：　2Byteライト[0x40,0x00]
 - データ読み出し　：　0x00をライト後6Byteリードする　

### 動作波形
- 初期化
![初期化.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478045/b8e42b7f-8e32-c038-09a8-7183e057941a.png)

- データ読み出し時の0x00ライト
![リクエスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478045/cbdb6ceb-5ddd-8116-843c-25c962387155.png)

- データ読み出し
![read.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478045/25aa2119-8279-a434-68c6-c05129931eec.png)

### リードデータ仕様
#### 暗号化について
　データは暗号化されているため以下の数式を使用して複合する
　_result =  (input ^ 0x17) + 0x17_

#### リードデータ内部

|リードデータ|bit割付|説明|備考|
|:--:|:---:|:---:|:---:|
|1Byte目|[7:0]|ジョイスティックX軸|センター値130、左に傾けると減少|
|2Byte目|[7:0]|ジョイスティックY軸|センター値130、下に傾けると減少|
|3Byte目|[7:0]|加速度センサX軸|[9:2]bitの値(下位2bitは6Byte目に存在)|
|4Byte目|[7:0]|加速度センサY軸|[9:2]bitの値(下位2bitは6Byte目に存在)|
|5Byte目|[7:0]|加速度センサZ軸|[9:2]bitの値(下位2bitは6Byte目に存在)|
|6Byte目|[7:6]<br>[5:4]<br>[3:2]<br>[1]<br>[0]|加速度センサZ軸<br>加速度センサY軸<br>加速度センサX軸<br>Cボタン<br>Zボタン|[1:0]bit目<br>[1:0]bit目<br>[1:0]bit目<br>押下時 0<br>押下時 0|


## ソースコード
　ライブラリ以外の部分のソースは以下のようになる

```C
// ヌンチャク構造体宣言
typedef struct {
	uint8_t joy_x;
	uint8_t joy_y;
	uint8_t button_c;
	uint8_t button_z;
	uint16_t accel_x;
	uint16_t accel_y;
	uint16_t accel_z;
}wiiNunchuck;
// HALライブラリでは7bitアドレスを左シフトして設定する
#define WII_NUNCHUCK_DEV_ID ((0x52)<<1)

// ディレイ関数、置き換えやすいように関数化
static void userDelay(uint32_t millisec)
{
	osDelay(millisec);
}

// データリード前に0x00をライト
static void send_request(I2C_HandleTypeDef *hi2c)
{
	uint8_t pData[2] = {0x00, 0x00};
	HAL_I2C_Master_Transmit(hi2c, WII_NUNCHUCK_DEV_ID, pData, 1, 3000);
}

// 複合
static uint8_t decode_byte(uint8_t x)
{
	return ((x ^ 0x17) + 0x17);
}

// データリード
void WiiNunchuck_getKey(I2C_HandleTypeDef *hi2c, wiiNunchuck *keyStatus)
{
    int cnt=0;
    uint8_t rxData[6],decData[6];

    send_request(hi2c);
    userDelay(2);

    // 6Byteリード
    HAL_I2C_Master_Receive(hi2c, WII_NUNCHUCK_DEV_ID|0x01 , rxData, 6, 3000);

    // データ複合
    for(cnt=0; cnt<6; cnt++){
    	decData[cnt] = decode_byte(rxData[cnt]);
    }

    // 構造体へ代入
    keyStatus->joy_x = decData[0];
    keyStatus->joy_y = decData[1];
    keyStatus->button_c = (decData[5]>>1) & 0x01;
    keyStatus->button_z = (decData[5]>>0) & 0x01;
    keyStatus->accel_x = (((uint16_t)decData[2])<<2) | ((decData[5]>>2) & 0x03);
    keyStatus->accel_y = (((uint16_t)decData[3])<<2) | ((decData[5]>>4) & 0x03);
    keyStatus->accel_z = (((uint16_t)decData[4])<<2) | ((decData[5]>>6) & 0x03);

}

// ヌンチャク初期化
void WiiNunchuck_init(I2C_HandleTypeDef *hi2c)
{
	uint8_t pData[2] = {0x40, 0x00};
	HAL_I2C_Master_Transmit(hi2c, WII_NUNCHUCK_DEV_ID, pData, 2, 3000);

}
```

## 動作結果
###出力例

| メンバ名 | 値 |
|:-:|:-:|
|joy_x|131|
|joy_y|131|
|button_c|1|
|button_z|1|
|accel_x|696|
|accel_y|509|
|accel_z|543|

## 総評
ボタンとスティックの情報は正常に取得できたが、
加速センサの値が正しいかどうか判断できない。
(傾けると重力加速度の方向の値が変わることは確認できるが…)