これは、PSoC Advent Calendar 2019の16日目に突っ込まれた記事です。
前回の記事では、Groveのスターターキットに入っていたGrove LCD RGB Backlightというボードを使ってLEDドライバを操作しました。これまでに使ってきたLCD表示とLEDドライバは、I2CのWRITEシーケンスのみを使っています。つまり、PSoCからデータを送りつけてばかりだったわけです。今回は、これまたスイッチサイエンスで購入した**絶対圧センサ評価モジュールからデータを取得して、Grove LCD RGB Backlight**に表示します。
絶対圧センサ
**絶対圧センサ評価モジュールとは、気圧を測定してくれるセンサを搭載したGroveコネクタ対応のボードです。この基板の上には、OMRON製の缶詰になった(canned)2SMPB-02E**という気圧センサとレギュレータなどが搭載されています。また、このセンサには、温度補償を行うための温度センサも内蔵しています。
気圧センサコンポーネント
これをどうやって操作するかと考えた結果、前回の記事と同じように、新たにコンポーネントを作成してしまう事にしました。
I2C Masterを操作するためのソフトウェアコンポーネントを作成しました。前回の記事と全く同じ考え方です。
API
このソフトウェア・コンポーネントには、以下のAPIを実装しました。
API | 概要 |
---|---|
void I2C_SENSOR_Init(void) | コンポーネントの初期設定を行います。 |
void I2C_SENSOR_Enable(void) | センサの動作を開始します。 |
void I2C_SENSOR_Disable(void) | センサの動作を停止します。 |
void I2C_SENSOR_Start(void) | コンポーネントの使用を開始します。 |
void I2C_SENSOR_Stop(void) | コンポーネントの使用を終了します。 |
void I2C_SENSOR_ReadCoefficient(void) | センサから補償パラメータを読みだしてコンポーネントに格納します。 |
uint32 I2C_SENSOR_IsMeasuring(void) | センサが測定中かどうかを判定します。まだ実装されていません。 |
void I2C_SENSOR_Sleep(void) | センサをSleepモードに遷移させます。 |
void I2C_SENSOR_Force(void) | センサに1回だけ測定させます。 |
void I2C_SENSOR_ReadRawData(void) | センサから測定したデータを取り出しコンポーネントに格納します。 |
double I2C_SENSOR_GetTemperature(void) | 測定データから温度を計算して返します。 |
double I2C_SENSOR_GetPressure(void) | 測定データから気圧を計算して返します。 |
まだ、このプロジェクトで使用できるだけのAPIしか実装していません。
I2CのREADシーケンス
このコンポーネントでは、I2CインターフェイスのWRITEシーケンスに加えてREADシーケンスを使用しています。そのために、内部関数I2C_SENSOR_ReceiveSequence()を定義して使用しています。
static uint32 I2C_SENSOR_ReceiveSequence(uint8 *rbuf, uint32 length) {
// Clear status flags
I2C_SENSOR_MasterClearStatus();
// Receive a I2C packet
(void) I2C_SENSOR_MasterReadBuf(
I2C_SENSOR_address,
rbuf,
length,
I2C_SENSOR_MODE_COMPLETE_XFER
);
while (!(I2C_SENSOR_MasterReadStatus() & I2C_SENSOR_READ_COMPLETE)) {
// Wait until I2C Master finishes transaction
}
// Return the data length
return I2C_SENSOR_MasterGetReadBufSize();
}
最初にステータスフラグをクリアします。そして、I2C Masterに対してREADシーケンスを開始させます。そのあと、ステータスフラグのREAD_COMPLETEフラグをポーリングしてREADシーケンスの終了を検出します。最後にREADシーケンスで実際に受信したデータのバイト数を関数の返り値とします。
メインルーチン
ここで使用したメインルーチンは、以下の通りです。
# include "project.h"
# include <stdio.h>
int main(void) {
CyGlobalIntEnable; /* Enable global interrupts. */
I2CM_Start();
// Setup LCD display
I2C_LCD_Start();
// Setup Backlight LED
I2C_LED_Start();
I2C_LED_WritePWM(0x33, 0x33, 0x88, 0x00);
// Startup Pressure Sensor
I2C_SENSOR_Start();
for (;;) {
double temp;
double pressure;
char sbuf[64];
I2C_SENSOR_Force();
CyDelay(1000);
I2C_SENSOR_ReadRawData();
temp = I2C_SENSOR_GetTemperature();
pressure = I2C_SENSOR_GetPressure();
sprintf(sbuf, "T:%7.2fdegC", temp);
I2C_LCD_Position(0, 0x80); // locate to 0,0
I2C_LCD_PrintString(sbuf);
sprintf(sbuf, "P:%7.2fmBar", pressure);
I2C_LCD_Position(0, 0xA8); // locate to 1,0
I2C_LCD_PrintString(sbuf);
}
}
使い方は、簡単です。まず、I2C_SENSOR_Force()
でセンサに温度と気圧のデータを取得させます。データの取得までに少々時間がかかります。本来であれば、測定中フラグを監視して測定終了を待つところですが、ここでは、ソフトウェアディレイを使って1秒間待っています。
取得したデータは、I2C_SENSOR_ReadRawData()
でセンサから読みだしてコンポーネントに格納します。
ここで取得したデータは、そのままでは使えません。センサが持っている補償データをもとに温度補償と非線形補償を加える必要があります。これらの計算を行うのがI2C_SENSOR_GetTemperature()
とI2C_SENSOR_GetPressure()
です。これらの関数はdouble
で値を返すので、それをLCDに表示させたら一回分の表示が終了します。
LCDのカーソル動作が変だ
このプロジェクトを作成していて、I2C_LCDコンポーネントのカーソル移動APII2C_LCD_Position()
がうまく機能していないことがわかりました。0行0桁に移動するにはI2C_LCD_Position(0,0)
とすれば良いはずなのですが、移動してくれません。API関数を見たところ、どうも実装されているコマンドが違っている様子です。
仕方ないので、I2C_LCD_Position(0, 0x80)
と妙な指定をして回避しました。1行目に移動するときはI2C_LCD_Position(0, 0xA8)
としています。どうも、I2C_LCDコンポーネントも自前で作り直さなきゃいけないみたいですね。
補償計算がややこしい
**"main.c"**を見ると、簡単に使えてしまっているように見えますが、内部では、かなりの計算をこなしています。気圧を計算するためのややこしい部分はすべてコンポーネントが隠してしまったのでした。
double I2C_SENSOR_GetTemperature(void) {
struct I2C_SENSOR_Context *p = &I2C_SENSOR_context;
p->tr = (p->a2 * p->dt + p->a1) * p->dt + p->a0;
p->temp = p->tr / 256.0;
return p->temp;
}
double I2C_SENSOR_GetPressure(void) {
struct I2C_SENSOR_Context *p = &I2C_SENSOR_context;
double x3 = p->bp3;
double x2 = p->b21 * p->tr + p->bp2;
double x1 = (p->b12 * p->tr + p->b11) * p->tr + p->bp1;
double x0 = (p->bt2 * p->tr + p->bt1) * p->tr + p->b00;
p->pr = ((x3 * p->dp + x2) * p->dp + x1) * p->dp + x0;
p->pressure = p->pr / 100.0;
return p->pressure;
}
これらのAPI関数は、センサから取り出したデータを基に温度と気圧の補償計算を行います。
これ以外にも、補償計算に使用する係数を計算するためのAPI関数がありますが、ユーザに見えないところで処理されています。
GitHub リポジトリ
関連サイト
32-bit Arm® Cortex®-M0 PSoCR 4
PSoCR 4 BLE (Bluetooth Smart)
スイッチサイエンスのスターターキットのページ
スイッチサイエンスの絶対圧センサ評価モジュールのページ
関連記事
CY8CKIT-042-BLE-A でLチカ
CY8CKIT-042-BLE-A でLCD表示
CY8CKIT-042-BLE-A でバックライト点灯
CY8CKIT-042-BLE-A で気圧を表示
CY8CKIT-042-BLE-A で気圧を送信