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();