前回、coutの利用を勉強しました。最後で、気圧センサLPS22HBの気圧を表示しましたが、それをまず、関数にして読みやすくします。coutの利用に変化はありません。
関数化を図る
wakeupは一度きりなので、init()関数にし、気圧データを読み取るreadData()を前のプログラムから分離しました。
#include <iostream>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>
#define lps22hbAdr 0x5d
int init();
float readData();
float press;
int fd;
int main(){
std::cout << "\nstart LPS22HB data read \n";
init();
press = readData();
std::cout << "press is " << press << "hPa\n";
return 0;
}
int init(){
fd = open("/dev/i2c-1", O_RDWR);
struct i2c_msg msg[1]; // /usr/include/linux/i2c.h
struct i2c_rdwr_ioctl_data packets; // /usr/include/linux/i2c-dev.h
unsigned char data[2];
// wake up
msg[0].addr = lps22hbAdr; // address
msg[0].flags = 0; // read、writeやアドレス長の指定
msg[0].len = 2; // bufに指定するdataのサイズ
msg[0].buf = data;
data[0] = 0x10; // レジスタアドレス
data[1] = 0x10; // 書き込み値;5Hz
packets.msgs = msg;
packets.nmsgs = 1; // msgのサイズ指定
int ret = ioctl(fd, I2C_RDWR, &packets);
std::cout << "\nsend: 0x" << std::hex << msg[0].addr << " - 0x"
<< std::hex << (int)data[0] << " - 0x"
<< std::hex << (int)data[1] << "\n";
return 0;
}
float readData(){
// read press data
struct i2c_msg msgP[4];
struct i2c_rdwr_ioctl_data packetsP;
unsigned char data1[4], data2, data3, data4;
msgP[0].addr = lps22hbAdr;
msgP[0].flags = 0; // 最初はアドレスを書き込み
msgP[0].len = 1;
msgP[0].buf = data1;
data1[0] = 0x8a; // 0x28 | 0x80 MSB data
msgP[1].addr = lps22hbAdr;
msgP[1].flags = I2C_M_RD; // リード時に設定
msgP[1].len = 1; // データを1byte読み出し
msgP[1].buf = &data2; // data2に読み出しデータが入る
data1[0] = 0xa9; // mid
msgP[2].addr = lps22hbAdr;
msgP[2].flags = 1;
msgP[2].len = 1; // データを1byte読み出し
msgP[2].buf = &data3; // data3に読み出しデータが入る
data1[0] = 0xaa; // LSB
msgP[3].addr = lps22hbAdr;
msgP[3].flags = 1;
msgP[3].len = 1; // データを1byte読み出し
msgP[3].buf = &data4; // data4に読み出しデータが入る
packetsP.msgs = msgP;
packetsP.nmsgs = 4; // アドレス書き込みとデータ読み出しでmsgは4
int retP = ioctl(fd, I2C_RDWR, &packetsP);
return press = std::stof( std::to_string((data2 << 16) | (data3 <<8) | (data4) )) / 4096;
}
温度も測定する
気圧センサLPS22HBの気圧値は、温度に影響を受けるので、MEMSデバイス自体が温度を測定して補正に利用します。ついでに、温度も外部へ出力されます。BME280などでは、気温の温度が出力されますが、このLPS22HBは、デバイスの温度が出力されるようで、気温より数度高めになります。
最初、
tupleを使って、気圧と温度の測定値を戻してもらう関数readData()を書いたのですが、bme280の時と違って、mainの後に関数を書いたので、プロトタイプ関数の宣言を最初にしないといけません。が、エラーが出て、対処方法がわかりませんし、検索しても誰も質問していません。
なので、気圧と温度は別々の関数にして、それぞれ、測定値を戻してもらうように書きました。
#include <iostream>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>
#define lps22hbAdr 0x5d
float press;
float temp;
int init();
float read_pressData();
float read_tempData();
int fd;
int main(){
std::cout << "\nstart LPS22HB data read \n";
init();
std::cout << "press is " << read_pressData() << "hPa\n";
std::cout << "temp is " << read_tempData() << "`C\n";
return 0;
}
int init(){
fd = open("/dev/i2c-1", O_RDWR);
struct i2c_msg msg[1]; // /usr/include/linux/i2c.h
struct i2c_rdwr_ioctl_data packets; // /usr/include/linux/i2c-dev.h
unsigned char data[2];
// wake up
msg[0].addr = lps22hbAdr; // address
msg[0].flags = 0; // read、writeやアドレス長の指定
msg[0].len = 2; // bufに指定するdataのサイズ
msg[0].buf = data;
data[0] = 0x10; // レジスタアドレス
data[1] = 0x10; // 書き込み値;5Hz
packets.msgs = msg;
packets.nmsgs = 1; // msgのサイズ指定
int ret = ioctl(fd, I2C_RDWR, &packets);
std::cout << "\nsend: 0x" << std::hex << msg[0].addr << " - 0x"
<< std::hex << (int)data[0] << " - 0x"
<< std::hex << (int)data[1] << "\n";
return 0;
}
float read_pressData(){
// read press data
struct i2c_msg msgP[4];
struct i2c_rdwr_ioctl_data packetsP;
unsigned char data1[4], data2, data3, data4;
msgP[0].addr = lps22hbAdr;
msgP[0].flags = 0; // 最初はアドレスを書き込み
msgP[0].len = 1;
msgP[0].buf = data1;
data1[0] = 0xa8; // 0x28 | 0x80 MSB data
msgP[1].addr = lps22hbAdr;
msgP[1].flags = I2C_M_RD; // リード時に設定
msgP[1].len = 1; // データを1byte読み出し
msgP[1].buf = &data2; // data2に読み出しデータが入る
data1[0] = 0xa9; // mid
msgP[2].addr = lps22hbAdr;
msgP[2].flags = 1;
msgP[2].len = 1; // データを1byte読み出し
msgP[2].buf = &data3; // data3に読み出しデータが入る
data1[0] = 0xaa; // LSB
msgP[3].addr = lps22hbAdr;
msgP[3].flags = 1;
msgP[3].len = 1; // データを1byte読み出し
msgP[3].buf = &data4; // data4に読み出しデータが入る
packetsP.msgs = msgP;
packetsP.nmsgs = 4; // アドレス書き込みとデータ読み出しでmsgは4
int retP = ioctl(fd, I2C_RDWR, &packetsP);
press = std::stof( std::to_string((data2 << 16) | (data3 <<8) | (data4) )) / 4096;
return press;
}
float read_tempData(){
// read temp data
struct i2c_msg msgT[3];
struct i2c_rdwr_ioctl_data packetsT;
unsigned char data1[2], data2, data3;
msgT[0].addr = lps22hbAdr;
msgT[0].flags = 0; // 最初はアドレスを書き込み
msgT[0].len = 1;
msgT[0].buf = data1;
data1[0] = 0xab; // 0x2b | 0x80 MSB data
msgT[1].addr =lps22hbAdr;
msgT[1].flags = I2C_M_RD; // リード時に設定
msgT[1].len = 1; // データを1byte読み出し
msgT[1].buf = &data2; // data2に読み出しデータが入る
data1[0] = 0xac; // LSB
msgT[2].addr = lps22hbAdr;
msgT[2].flags = 1;
msgT[2].len = 1; // データを1byte読み出し
msgT[2].buf = &data3; // data3に読み出しデータが入る
packetsT.msgs = msgT;
packetsT.nmsgs = 3; // アドレス書き込みとデータ読み出しでmsgは4
int retT = ioctl(fd, I2C_RDWR, &packetsT);
int tempNative = (data2 <<8) | data3;
int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
temp = std::stof( std::to_string(temp2scomp)) / 100;
return temp;
}
実行します。
$ ./a.out
start LPS22HB data read
send: 0x5d - 0x10 - 0x10
press is 1008.7hPa
temp is 28.57`C
coutの勉強をしてるんです その1
ということで、2の補数計算をしているときの、ビットの表示をしてみることにしました。
気圧と温度のどちらも読み出したデータは2の補数形式です。でも、気圧は自然界ではマイナスの値をとらないので、温度のほうだけ対処しています。
温度のデータは16ビットです。2の補数は、最上位ビットを見ます。'1'だったら、マイナスの符号をつけます。
なお、データシートに従って、読み出した値を100で割ると摂氏表示の温度が得られます。
int tempNative = (data2 <<8) | data3;
int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
temp = std::stof( std::to_string(temp2scomp)) / 100;
テスト用ソースを作ります。
#include <iostream>
int main(){
int tempNative = -2345;
int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
float temp = std::stof( std::to_string(temp2scomp)) / 100;
std::cout << "temp is " <<temp << "\n";
return 0;
}
実行します。
$ g++ ex006.cpp
$ ./a.out
temp is -23.45
-2345のマイナスをとって2345に変更して実行します。
$ g++ ex006.cpp
$ ./a.out
temp is 23.45
途中経過を表示します。
#include <iostream>
#include <iomanip>
//#include <iosfwd>
#include <bitset>
int main(){
int tempNative = -2345;
int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
float temp = std::stof( std::to_string(temp2scomp)) / 100;
std::cout << "tempNative is " << std::dec << tempNative
<< " 0b" << std::bitset<16>(tempNative)
<< " 0b" << ((std::bitset<16>(tempNative)).to_string()).insert(4, " ").insert(9, " ").insert(14, " ")
<< "\n";
std::cout << "temp2scomp is " << std::dec << temp2scomp
<< " 0b" << std::bitset<16>(temp2scomp)
<< " 0b" << ((std::bitset<16>(temp2scomp)).to_string()).insert(4, " ").insert(9, " ").insert(14, " ")
<< "\n";
std::cout << "temp is " << temp << "\n";
std::cout << "temp is " << std::fixed << std::setprecision(1) << temp << "`C\n";
return 0;
}
実行します。
$ g++ ex006.cpp
$ ./a.out
tempNative is -2345 0b1111011011010111 0b1111 0110 1101 0111
temp2scomp is -2345 0b1111011011010111 0b1111 0110 1101 0111
temp is -23.45
temp is -23.5`C
テスト・データを正の数値にして実行します。
$ ./a.out
tempNative is 2345 0b0000100100101001 0b0000 1001 0010 1001
temp2scomp is 2345 0b0000100100101001 0b0000 1001 0010 1001
temp is 23.45
temp is 23.5`C
coutの勉強をしてるんです その2
気圧の出力がpress is 1008.7hPa
のように、小数点第1位まで表示されていますが、特に桁数の指定はしていません。台風の時は1000hPaを切りますが、テレビでは、温度や湿度は小数点以下の表示をしているのに、気圧は整数値です。
台風の気圧は直接測れないので、衛星写真を撮って、目視で大きさ(直径)を測って気圧を導き出しています。
温度計は、どこかの団体に手数料を払って、精度?の保証をしてもらっているんですけどね。
任意の小数点の桁数表示にチャレンジします。
というか、前のプログラムで実装しました。
setprecision(1)
のかっこの中の数字が、小数点第何位まで表示するかを指定しています。
#include <iostream>
#include <iomanip>
//#include <iosfwd>
#include <bitset>
int main(){
int tempNative = -2345;
int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
float temp = std::stof( std::to_string(temp2scomp)) / 100;
std::cout << "tempNative is " << std::dec << tempNative
<< " 0b" << std::bitset<16>(tempNative)
<< " 0b" << ((std::bitset<16>(tempNative)).to_string()).insert(4, " ").insert(9, " ").insert(14, " ")
<< "\n";
std::cout << "temp2scomp is " << std::dec << temp2scomp
<< " 0b" << std::bitset<16>(temp2scomp)
<< " 0b" << ((std::bitset<16>(temp2scomp)).to_string()).insert(4, " ").insert(9, " ").insert(14, " ")
<< "\n";
std::cout << "temp is " << temp << "\n";
std::cout << "temp is " << std::fixed << std::setprecision(1) << temp << "`C\n";
std::cout << "temp is " << std::setprecision(1) << temp << "`C\n";
std::cout << "temp is " << std::fixed << std::setprecision(0) << temp << "`C\n";
std::cout << "temp is " << std::setprecision(0) << temp << "`C\n";
return 0;
}
std::fixed
を使わない例もあるようです。実行しました。
$ g++ ex006.cpp
$ ./a.out
tempNative is -2345 0b1111011011010111 0b1111 0110 1101 0111
temp2scomp is -2345 0b1111011011010111 0b1111 0110 1101 0111
temp is -23.45
temp is -23.5`C
temp is -23.5`C
temp is -23`C
temp is -23`C
どうも、丸められているように見えますが、解説にはそのようなことは書かれていませんでした。
<ios>にあったbitset
が、<bitset>にいつどういう理由で移動したかも見つけられていません。