PSoC : I2C + Capsense Tuner 2023/11/27

PSoCのタッチセンサのCapsenseTunerにはUARTとI2Cがあります(最近RTTも増えましたがまだ理解できておらず・・)。自分はいつも、ArduinoやPICと連携しやすいのでUARTを使っていたのですが、I2Cのほうが通信速度が速いので今回使ってみました。

UARTでもI2Cでも、センサ情報を読み出したり、コマンドを送信することで、一時停止、一時停止解除、リスタートなど操作することもできます。


センサ情報の読み出し

送られてくるデータがとても多いので、何番目にセンサ情報があるのか知る必要があります。

「cycfg_capsense.h」にある構造体を見ると、大きく3種類に分かれていて、commonContext、widgetContext、sensorContextがあります。

さらに「cy_capsense_structure.h」には、commonContext、widgetContext、sensorContextの中身が書いてあり、変数名とバイト数がわかります。

数えていくと、commonContextは24バイト固定で、widgetContextが56バイト、sensorContextは10バイトで、センサの数ぶんありました。

タッチセンサの情報をまとめたものは「sensorContext」にあり、このsensorContextが何番目のアドレスなのかを計算します。

sensorContextの先頭アドレス
(common=24)+(widget=56)×(センサ数=3)

さらに、sensorContextは、今回はセンサ数が3なので、10バイトずつ3つあります。10バイトは以下のようになっています。「diff」が反応前後の差の数値、「status」がタッチしたかどうか(0または1)の情報になります。

typedef struct {
  uint16_t raw;
  uint16_t bsln;
  uint16_t diff;
  uint8_t status;
  uint8_t negBslnRstCnt;
  uint8_t idacComp;
  uint8_t bslnExt;
} Sensor;

3つのセンサまでのアドレスはそれぞれ以下のように計算します。
sensorContextの先頭アドレス+(sensorContext=10)×(センサ番号=0)
sensorContextの先頭アドレス+(sensorContext=10)×(センサ番号=1)
sensorContextの先頭アドレス+(sensorContext=10)×(センサ番号=2)

ArduinoのI2C読み出しのプログラム

#include <Wire.h>

//タッチセンサ内部のデータアドレス
#define COMMON_SIZE 24
#define WIDGET_SIZE 56
#define SENSOR_SIZE 10
#define START_ADDRESS (COMMON_SIZE + WIDGET_SIZE * TOTAL_TOUCH)

//タッチセンサの数
#define TOTAL_TOUCH 3

//センサ情報の定義
typedef struct {
  uint16_t raw;
  uint16_t bsln;
  uint16_t diff;
  uint8_t status;
  uint8_t negBslnRstCnt;
  uint8_t idacComp;
  uint8_t bslnExt;
} Sensor;
Sensor s[TOTAL_TOUCH];

//データ読出し用バッファ
uint8_t buff[10];

void setup() {
  Wire.begin();
  Serial.begin(115200);
}

void loop() {
  for (int i = 0; i < TOTAL_TOUCH; i++) {
    //I2Cアドレス : 8
    Wire.beginTransmission(8);
    //タッチセンサ内部のデータ読み出しアドレスを指定
    uint16_t address = (START_ADDRESS + SENSOR_SIZE * i);
    Wire.write((address >> 8) & 0xFF);
    Wire.write((address)&0xFF);
    Wire.endTransmission();
    //I2Cアドレス 8番から、10バイトの読出しリクエスト
    Wire.requestFrom(8, 10);
    //buff配列に格納する
    int pos = 0;
    while (Wire.available()) {
      uint8_t c = Wire.read();
      buff[pos++] = c;
    }

    //生データ
    s[i].raw = buff[1] << 8 | buff[0];
    //ベースライン(周囲の静電容量変化に追随する値)
    s[i].bsln = buff[3] << 8 | buff[2];
    //生データとベースラインの差
    s[i].diff = buff[5] << 8 | buff[4];
    //タッチしたかどうか(0か1)
    s[i].status = buff[6];

    //ベースラインの計算用
    s[i].negBslnRstCnt = buff[7];  //タッチしなくなった瞬間からカウントする
    s[i].idacComp = buff[8];       //自動調整:タッチの判別でつかう電流値(uA)
    s[i].bslnExt = buff[9];        //タッチしたままになったとき、ベースラインをリセットするために使う値
  }

  for (int i = 0; i < TOTAL_TOUCH; i++) {
    // Serial.print(s[i].raw);        //生データ
    // Serial.print(",");
    // Serial.print(s[i].bsln);       //ベースライン
    // Serial.print(",");
    Serial.print(s[i].diff);  //近接値(生データがベースラインを超えた時の差分)
    Serial.print(",");
    Serial.print(s[i].status);  //タッチしたかどうか
    Serial.print(",");
    // Serial.print(s[i].negBslnRstCnt); //ベースラインの計算用(タッチしなくなった瞬間からカウントする)
    // Serial.print(",");
    // Serial.print(s[i].idacComp);      //自動調整:タッチの判別でつかう電流値(uA)
    // Serial.print(",");
    // Serial.print(s[i].bslnExt);       //タッチしたままになったとき、ベースラインをリセットするために使う値
    // Serial.print(",");
    Serial.print("\t");
  }
  Serial.println();
  delay(10);
}

センサへの書込み

次にセンサの一時停止、一時停止解除、リスタートなど、センサにコマンドを送って操作する方法です。

commonContextの中にあるコマンドとなる変数を書き換えることで動作します。この変数がどこにあるのか、知る必要がありますが、割と見つけやすいです。

「cy_capsense_structure.h」にある「tunerCmd」がコマンドとなる変数です。

参考までに「cycfg_capsense.c」にも初期値が定義してあります。

「tunerCmd」が、何番目のアドレスか調べます。直前にある「configId」が2バイトあり、先頭でもあるので「tunerCmd」のアドレスは2になります。

コマンドの種類は「cy_capsense_structure.h」にあります。

一時停止は「1」、一時停止解除は「2」、リスタートは「3」など、あります。

他のコマンドが何をするのかは「cy_capsense_tuner.c」にあります。

この中で「リスタート」を行なうと感度リセットされます。

ArduinoのI2C読出し&書込みプログラム

「リスタート」コマンドを組み込んだArduinoのプログラムになります。最初にリスタートし、その後はシリアル通信で「r」または「R」を送信すると、リスタートします。

#include <Wire.h>

//タッチセンサ内部のデータアドレス
#define COMMON_SIZE 24
#define WIDGET_SIZE 56
#define SENSOR_SIZE 10
#define START_ADDRESS (COMMON_SIZE + WIDGET_SIZE * TOTAL_TOUCH)

//タッチセンサの数
#define TOTAL_TOUCH 3

//センサ情報の定義
typedef struct {
  uint16_t raw;
  uint16_t bsln;
  uint16_t diff;
  uint8_t status;
  uint8_t negBslnRstCnt;
  uint8_t idacComp;
  uint8_t bslnExt;
} Sensor;
Sensor s[TOTAL_TOUCH];

//データ読出し用バッファ
uint8_t buff[10];

void setup() {
  Wire.begin();
  Serial.begin(115200);

  //タッチセンサのリスタート
  restartTouchsensor();
}

void loop() {
  for (int i = 0; i < TOTAL_TOUCH; i++) {
    //I2Cアドレス : 8
    Wire.beginTransmission(8);
    //タッチセンサ内部のデータ読み出しアドレスを指定
    uint16_t address = (START_ADDRESS + SENSOR_SIZE * i);
    Wire.write((address >> 8) & 0xFF);
    Wire.write((address)&0xFF);
    Wire.endTransmission();
    //I2Cアドレス 8番から、10バイトの読出しリクエスト
    Wire.requestFrom(8, 10);
    //buff配列に格納する
    int pos = 0;
    while (Wire.available()) {
      uint8_t c = Wire.read();
      buff[pos++] = c;
    }

    //生データ
    s[i].raw = buff[1] << 8 | buff[0];
    //ベースライン(周囲の静電容量変化に追随する値)
    s[i].bsln = buff[3] << 8 | buff[2];
    //生データとベースラインの差
    s[i].diff = buff[5] << 8 | buff[4];
    //タッチしたかどうか(0か1)
    s[i].status = buff[6];

    //ベースラインの計算用
    s[i].negBslnRstCnt = buff[7];  //タッチしなくなった瞬間からカウントする
    s[i].idacComp = buff[8];       //自動調整:タッチの判別でつかう電流値(uA)
    s[i].bslnExt = buff[9];        //タッチしたままになったとき、ベースラインをリセットするために使う値
  }

  for (int i = 0; i < TOTAL_TOUCH; i++) {
    // Serial.print(s[i].raw);        //生データ
    // Serial.print(",");
    // Serial.print(s[i].bsln);       //ベースライン
    // Serial.print(",");
    Serial.print(s[i].diff);  //近接値(生データがベースラインを超えた時の差分)
    Serial.print(",");
    Serial.print(s[i].status);  //タッチしたかどうか
    Serial.print(",");
    // Serial.print(s[i].negBslnRstCnt); //ベースラインの計算用(タッチしなくなった瞬間からカウントする)
    // Serial.print(",");
    // Serial.print(s[i].idacComp);      //自動調整:タッチの判別でつかう電流値(uA)
    // Serial.print(",");
    // Serial.print(s[i].bslnExt);       //タッチしたままになったとき、ベースラインをリセットするために使う値
    // Serial.print(",");
    Serial.print("\t");
  }
  Serial.println();

  //シリアル通信の受信バッファにデータがあったら
  while (Serial.available() > 0) {
    //データを読み込み
    uint8_t c = Serial.read();
    //読み込んだデータが「r」または「R」のとき
    if (c == 'r' || c == 'R') {
      //タッチセンサのリスタート
      restartTouchsensor();
    }
  }

  delay(10);
}

//タッチセンサのリスタート
void restartTouchsensor() {
  //I2Cアドレス:8
  Wire.beginTransmission(8);
  //書込みアドレス:0x0002
  uint16_t address = 2;
  Wire.write((address >> 8) & 0xFF);
  Wire.write((address)&0xFF);
  //書込みデータ:0x0003...RESTART COMMAND
  Wire.write(0x03);  //restart_L
  Wire.write(0x00);  //restart_H
  Wire.endTransmission();
}

注意するところは、I2Cで書込みアドレスを指定する16ビットのアドレスは、8ビットに分けて上位・下位の順になりますが、書き込む16ビットデータは「8ビットに分けて下位・上位」となっています。これはPSoCのEZI2Cの仕様によるものです。

  //I2Cアドレス:8
  Wire.beginTransmission(8);
  //書込みアドレス:0x0002
  uint16_t address = 2;
  Wire.write((address >> 8) & 0xFF);
  Wire.write((address)&0xFF);
  //書込みデータ:0x0003...RESTART COMMAND
  Wire.write(0x03);  //restart_L
  Wire.write(0x00);  //restart_H
  Wire.endTransmission();