ここまで、汎用的に使えるi2cアクセス関数を作ってきました。
one_byte_write(uint8_t address, uint8_t resisterAdr, uint8_t setData)
one_byte_write_noaddress(uint8_t address, uint8_t setData)
マルチバイトが扱える関数を整理します。
環境
- Raspberry Pi 5 8GB
- 追加ボード;NVMe Base for Raspberry Pi 5 (NVMe Base by Pimoroni)
- Crucial クルーシャル P2シリーズ 500GB 3D NAND NVMe PCIe M.2 SSD CT500P2SSD8
- Ubuntu Desktop 24.04LTS(64-bit)
マルチバイト・ライト関数
clearDisplay(uint8_t address)
display(uint8_t address, uint8_t dispSegment, uint8_t dispData)
をベースに、汎用化します。
write_block_data(uint8_t address, uint8_t resisterAdr, uint8_t setData)
smbusライブラリでいうところの、
write_block_data(アドレス, コマンド, バイト数)
write_i2c_block_data(アドレス, コマンド, バイト数)
に相当します。上記の二つは区別して使うのだったかどうか忘れました。何か違ったような記憶があります。
アドレスの後、コマンドとバイト数と二つのパラメタがありますが、同じただのバイト値です。コマンド=レジスタ・アドレスが1バイト、もしくは2バイトに、送るべきバイト列の組み合わせです。バイト数は、その数になります。
いじっていたら、1本の書き込み関数で、済ませられるような気がしてきました。
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount)
#include <iostream>
#include <string>
#include <iomanip>
#include <cstdint>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#define white_ht16k33Adr 0x71
#define red_ht16k33Adr 0x70
#define HT16K33_GENERIC_SYSTEM_ON 0x21
#define HT16K33_GENERIC_DISPLAY_ON 0x81
#define HT16K33_GENERIC_CMD_BRIGHTNESS 0xe0
#define sht45Adr 0x44
int fd;
int systemOn(uint8_t address);
int displayOn(uint8_t address);
int brightnessSet(uint8_t address, uint8_t level);
int init_ht16k33();
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount);
int main(){
std::cout << "start display HT16K33\n";
fd = open("/dev/i2c-1", O_RDWR);
init_ht16k33();
uint8_t sendData[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
write_block_data(white_ht16k33Adr, sendData, sizeof(sendData));
uint8_t segment = 0; // 1
uint8_t data = 0x5b; // 2
uint8_t sendData1[] = {segment, data};
write_block_data(white_ht16k33Adr, sendData1, sizeof(sendData1));
segment = 2; // 2
data = 0x6d; // 5
uint8_t sendData2[] = {segment, data};
write_block_data(white_ht16k33Adr, sendData2, sizeof(sendData2));
segment = 6; // 3
data = 0x66; // 4
uint8_t sendData3[] = {segment, data};
write_block_data(white_ht16k33Adr, sendData3, sizeof(sendData3));
segment = 8; // 4
data = 0x07; // 7
uint8_t sendData4[] = {segment, data};
write_block_data(white_ht16k33Adr, sendData4, sizeof(sendData4));
sleep(5);
return 0;
}
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
std::cout << "\nsend data is 0x" << std::hex << (int)address << "\t";
for ( int i=0; i < byteCount; i++){
std::cout << i << ": 0x" << std::hex << (int)sendData[i] << ", ";
}
std::cout << "\n";
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = byteCount;
msg[0].buf = sendData;
packets.msgs = msg;
packets.nmsgs = 1; // データ書き込みでmsgは1
int ret = ioctl(fd, I2C_RDWR, &packets);
return 0;
}
int init_ht16k33(){
systemOn(white_ht16k33Adr); systemOn(red_ht16k33Adr);
usleep(1000);
displayOn(white_ht16k33Adr); displayOn(red_ht16k33Adr);
usleep(1000);
brightnessSet(white_ht16k33Adr, 8); brightnessSet(red_ht16k33Adr, 8);
usleep(1000);
std::cout << "\ninit ht16k33 \n";
return 0;
}
int systemOn(uint8_t address){
uint8_t sendData[] = {HT16K33_GENERIC_SYSTEM_ON, 0};
write_block_data(white_ht16k33Adr, sendData, sizeof(sendData));
return 0;
}
int displayOn(uint8_t address){
uint8_t sendData[] = {HT16K33_GENERIC_DISPLAY_ON, 0};
write_block_data(white_ht16k33Adr, sendData, sizeof(sendData));
return 0;
}
int brightnessSet(uint8_t address, uint8_t level){
uint8_t sendData[] = {HT16K33_GENERIC_CMD_BRIGHTNESS, level};
write_block_data(white_ht16k33Adr, sendData, sizeof(sendData));
return 0;
}
実行します。7セグメントLEDの表示をクリヤし、白色のほうに2547を表示します。
$ g++ ex133.cpp
$ ./a.out
start display HT16K33
send data is 0x71 0: 0x21, 1: 0x0,
send data is 0x71 0: 0x21, 1: 0x0,
send data is 0x71 0: 0x81, 1: 0x0,
send data is 0x71 0: 0x81, 1: 0x0,
send data is 0x71 0: 0xe0, 1: 0x8,
send data is 0x71 0: 0xe0, 1: 0x8,
init ht16k33
send data is 0x71 0: 0x0, 1: 0x0, 2: 0x0, 3: 0x0, 4: 0x0, 5: 0x0, 6: 0x0, 7: 0x0, 8: 0x0, 9: 0x0, a: 0x0,
send data is 0x71 0: 0x0, 1: 0x5b,
send data is 0x71 0: 0x2, 1: 0x6d,
send data is 0x71 0: 0x6, 1: 0x66,
send data is 0x71 0: 0x8, 1: 0x7,
今まで学んできた、配列のポインタ渡しです。
配列はいくつかの例外を除いて常にポインタ型に暗黙変換されます。このため配列を参照する時に、 [] を省略すると、配列の先頭を指し示すポインタが取得できます。
関数の引数宣言で配列型をつかうとき、ポインタ型として解釈される
出典;
読み出し
配列をポインタ渡しをします。そこまではいいのですが、その配列を書き換えたいのですが、普通に見つかりません。でも、できちゃったんです。
int read_block_data_noResister(uint8_t address, unsigned char * readBuf, uint8_t byteCount );
#include <iostream>
#include <string>
#include <iomanip>
#include <map>
#include <cstdint>
#include <array>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>
#include <tuple>
#define white_ht16k33Adr 0x71
#define red_ht16k33Adr 0x70
#define HT16K33_GENERIC_SYSTEM_ON 0x21
#define HT16K33_GENERIC_DISPLAY_ON 0x81
#define HT16K33_GENERIC_CMD_BRIGHTNESS 0xe0
#define sht45Adr 0x44
int fd;
//int read_block_data(uint8_t address, uint8_t regi, unsigned char *readBuf, uint8_t byteCount );
int read_block_data_noResister(uint8_t address, unsigned char * readBuf, uint8_t byteCount );
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount);
int main(){
std::cout << "start \n";
fd = open("/dev/i2c-1", O_RDWR);
uint8_t data0 = 0x94; // soft reset
uint8_t sendData0[] = {data0};
write_block_data(white_ht16k33Adr, sendData0, sizeof(sendData0));
usleep(1000);
uint8_t data1 = 0x89; // read serial number
uint8_t sendData1[] = {data1};
write_block_data(white_ht16k33Adr, sendData1, sizeof(sendData1));
usleep(10000);
std::cout << "\ninit sht45 \n";
unsigned char readBuf1[] = {0,0,0,0,0,0};
read_block_data_noResister(sht45Adr, readBuf1, sizeof(readBuf1) );
std::cout << "all data : 0x" << std::hex << (int)readBuf1[0] << " , 0x"
<< std::hex << (int)readBuf1[1] << " , 0x" << std::hex << (int)readBuf1[2] << " , 0x"
<< std::hex << (int)readBuf1[3] << " , 0x" << std::hex << (int)readBuf1[4] << " , 0x"
<< std::hex << (int)readBuf1[5] << "\n";
return 0;
}
/*
int read_block_data(uint8_t address, uint8_t regi, unsigned char *readBuf, uint8_t byteCount ){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = 1;
msg[0].buf = ®i;
msg[0].addr = address; // device address
msg[0].flags = I2C_M_RD; // アドレスを書き込み
msg[0].len = byteCount;
msg[0].buf = readBuf;
packets.msgs = msg;
packets.nmsgs = 2; // データ読み出しでmsgは2
int ret = ioctl(fd, I2C_RDWR, &packets);
return 0;
}
*/
int read_block_data_noResister(uint8_t address, unsigned char * readBuf, uint8_t byteCount ){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
unsigned char data[byteCount];
msg[0].addr = address; // device address
msg[0].flags = I2C_M_RD; // アドレスを書き込み
msg[0].len = byteCount;
msg[0].buf = &data[0];
packets.msgs = msg;
packets.nmsgs = 1; // データ読み出しでmsgは1
int ret = ioctl(fd, I2C_RDWR, &packets);
std::cout << "all READ data : 0x" << std::hex << (int)data[0] << " , 0x"
<< std::hex << (int)data[1] << " , 0x" << std::hex << (int)data[2] << " , 0x"
<< std::hex << (int)data[3] << " , 0x" << std::hex << (int)data[4] << " , 0x"
<< std::hex << (int)data[5] << "\n";
for (int i = 0; i <byteCount; i++){
readBuf[i] = data[i];
}
return 0;
}
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
std::cout << "\nsend data is 0x" << std::hex << (int)address << "\t";
for ( int i=0; i < byteCount; i++){
std::cout << i << ": 0x" << std::hex << (int)sendData[i] << ", ";
}
std::cout << "\n";
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = byteCount;
msg[0].buf = sendData;
packets.msgs = msg;
packets.nmsgs = 1; // データ書き込みでmsgは1
int ret = ioctl(fd, I2C_RDWR, &packets);
return 0;
}
実行します。
$ g++ ex135.cpp
$ ./a.out
send data is 0x71 0: 0x94,
send data is 0x71 0: 0x89,
init sht45
all READ data : 0x10 , 0x35 , 0x4b , 0xe6 , 0xff , 0xff
all data : 0x10 , 0x35 , 0x4b , 0xe6 , 0xff , 0xff
作ったのは、レジスタ内の読み出し関数int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount)です。
読み出した値は msg[0].buf = &data[0];に入っています。関数呼び出しで、unsigned char * readBufで配列のポインタを渡しています。
for (int i = 0; i <byteCount; i++){
readBuf[i] = data[i];
}
書き戻せたんです。これって配列同士でコピーをしているんではないような気がします。暗黙のポインタ同士の代入?置換? 暗黙のポインタ・アドレス同士の代入?
そんなことをするなら参照を使えと言われそうです。でも、Cの時代は使えなかったわけだけど、この手の解説が見つからないのはなぜ?
for (int i = 0; i <byteCount; i++){
*(readBuf+i) = *(data+i);
}
に変更して実行します。
$ g++ ex135.cpp
$ ./a.out
send data is 0x71 0: 0x94,
send data is 0x71 0: 0x89,
init sht45
init sht45
all READ data : 0x10 , 0x35 , 0x4b , 0xe6 , 0xff , 0xff
all data : 0x10 , 0x35 , 0x4b , 0xe6 , 0xff , 0xff
できました。配列の要素はアドレス順に配置されるのです。当たり前?
このポインタを使ったアドレス渡しを参照というかたちで解説しているのを見ます。言葉通り参照なのですが、厳密には擬似的に「参照による呼び出し」を実現しているという表現になるようです。
で、真の参照とはリファレンスなんだそうです。
すみません、理解できていません。
配列の参照渡しがむごいことに
前に構造体の参照渡しを勉強したとき、ポインタ渡しのほうが読みやすいなーという感想を持ちました。
で、検索の仕方が悪いのか、参照渡しは配列の内容を替えられるという話なのに、実際にそうしているのが見つかりません。探し方が悪いのか。
がんばって、上記のプログラムを参照渡しの変更しました。
int read_block_data_noResister(uint8_t address, unsigned char (&readBuf)[6], uint8_t byteCount){
が書きにくい、なぜ、[6]が必要なの?byteCountを書くと怒られるし。
for (int i = 0; i <byteCount; i++){
*(readBuf+i) = data[i];
}
この記述は、腸がねじれてしまったように見える。
#include <iostream>
#include <string>
#include <iomanip>
#include <map>
#include <cstdint>
#include <array>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>
#include <tuple>
#define white_ht16k33Adr 0x71
#define red_ht16k33Adr 0x70
#define HT16K33_GENERIC_SYSTEM_ON 0x21
#define HT16K33_GENERIC_DISPLAY_ON 0x81
#define HT16K33_GENERIC_CMD_BRIGHTNESS 0xe0
#define sht45Adr 0x44
int fd;
//int read_block_data(uint8_t address, uint8_t regi, unsigned char *readBuf, uint8_t byteCount );
int read_block_data_noResister(uint8_t address, unsigned char (&readBuf)[6], uint8_t byteCount);
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount);
int main(){
std::cout << "start \n";
fd = open("/dev/i2c-1", O_RDWR);
uint8_t data0 = 0x94; // soft reset
uint8_t sendData0[] = {data0};
write_block_data(white_ht16k33Adr, sendData0, sizeof(sendData0));
usleep(1000);
uint8_t data1 = 0x89; // read serial number
uint8_t sendData1[] = {data1};
write_block_data(white_ht16k33Adr, sendData1, sizeof(sendData1));
usleep(10000);
std::cout << "\ninit sht45 \n";
unsigned char readBuf1[] = {0,0,0,1,2,3};
read_block_data_noResister(sht45Adr, readBuf1, sizeof(readBuf1));
std::cout << "all data : 0x" << std::hex << (int)readBuf1[0] << " , 0x"
<< std::hex << (int)readBuf1[1] << " , 0x" << std::hex << (int)readBuf1[2] << " , 0x"
<< std::hex << (int)readBuf1[3] << " , 0x" << std::hex << (int)readBuf1[4] << " , 0x"
<< std::hex << (int)readBuf1[5] << "\n";
return 0;
}
/*
int read_block_data(uint8_t address, uint8_t regi, unsigned char *readBuf, uint8_t byteCount ){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = 1;
msg[0].buf = ®i;
msg[0].addr = address; // device address
msg[0].flags = I2C_M_RD; // アドレスを書き込み
msg[0].len = byteCount;
msg[0].buf = readBuf;
packets.msgs = msg;
packets.nmsgs = 2; // データ読み出しでmsgは2
int ret = ioctl(fd, I2C_RDWR, &packets);
return 0;
}
*/
int read_block_data_noResister(uint8_t address, unsigned char (&readBuf)[6], uint8_t byteCount){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
unsigned char data[byteCount];
msg[0].addr = address; // device address
msg[0].flags = I2C_M_RD; // アドレスを書き込み
msg[0].len = byteCount;
msg[0].buf = &data[0];
packets.msgs = msg;
packets.nmsgs = 1; // データ読み出しでmsgは1
int ret = ioctl(fd, I2C_RDWR, &packets);
// *readBuf = data[byteCount];
std::cout << "all READ data : 0x" << std::hex << (int)data[0] << " , 0x"
<< std::hex << (int)data[1] << " , 0x" << std::hex << (int)data[2] << " , 0x"
<< std::hex << (int)data[3] << " , 0x" << std::hex << (int)data[4] << " , 0x"
<< std::hex << (int)data[5] << "\n";
for (int i = 0; i <byteCount; i++){
*(readBuf+i) = data[i];
}
return 0;
}
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
std::cout << "\nsend data is 0x" << std::hex << (int)address << "\t";
for ( int i=0; i < byteCount; i++){
std::cout << i << ": 0x" << std::hex << (int)sendData[i] << ", ";
}
std::cout << "\n";
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = byteCount;
msg[0].buf = sendData;
packets.msgs = msg;
packets.nmsgs = 1; // データ書き込みでmsgは1
int ret = ioctl(fd, I2C_RDWR, &packets);
return 0;
}
実行します。
$ g++ ex136.cpp
$ ./a.out
send data is 0x71 0: 0x94,
send data is 0x71 0: 0x89,
init sht45
all READ data : 0xe0 , 0xf7 , 0x4a , 0xd5 , 0xff , 0xff
all data : 0xe0 , 0xf7 , 0x4a , 0xd5 , 0xff , 0xff
そもそも、この読みだしプログラム、読み出すのが1バイトであったら、returnで戻せばよくない?、複数ならtupleで戻せばよくない?配列を渡すということが何も必然ではない、な!
何バイトでも対応できるというのがメリットにはなっているが。
もう一つのレジスタ指定読み出し関数
使い慣れた気圧センサLPS22HBをつなぎます。
$ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- 44 -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- 5d -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: 70 71 -- -- -- -- -- --
0x44がsht45、0x5dがlps22hb、0x70と0x71が7セグメントLEDです。
#include <iostream>
#include <string>
#include <iomanip>
#include <cstdint>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#define white_ht16k33Adr 0x71
#define red_ht16k33Adr 0x70
#define HT16K33_GENERIC_SYSTEM_ON 0x21
#define HT16K33_GENERIC_DISPLAY_ON 0x81
#define HT16K33_GENERIC_CMD_BRIGHTNESS 0xe0
#define sht45Adr 0x44
#define lps22hbAdr 0x5d
int fd;
int read_block_data(uint8_t address, unsigned char regi, unsigned char * readBuf, uint8_t byteCount );
int read_block_data_noResister(uint8_t address, unsigned char * readBuf, uint8_t byteCount );
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount);
int main(){
std::cout << "start lps22hb\n";
fd = open("/dev/i2c-1", O_RDWR);
uint8_t data0 = 0x94; // soft reset
uint8_t sendData0[] = {data0};
write_block_data(white_ht16k33Adr, sendData0, sizeof(sendData0));
usleep(1000);
uint8_t data1 = 0x89; // read serial number
uint8_t sendData1[] = {data1};
write_block_data(white_ht16k33Adr, sendData1, sizeof(sendData1));
usleep(10000);
std::cout << "\ninit sht45 \n";
unsigned char readBuf1[] = {0,0,0,1,2,3};
read_block_data_noResister(sht45Adr, readBuf1, sizeof(readBuf1));
std::cout << "all data : 0x" << std::hex << (int)readBuf1[0] << " , 0x"
<< std::hex << (int)readBuf1[1] << " , 0x" << std::hex << (int)readBuf1[2] << " , 0x"
<< std::hex << (int)readBuf1[3] << " , 0x" << std::hex << (int)readBuf1[4] << " , 0x"
<< std::hex << (int)readBuf1[5] << "\n";
unsigned char readResister = 0x10; // config resister
uint8_t data = 0x10; // wake up,5Hz
uint8_t sendData2[] = {readResister, data};
write_block_data(lps22hbAdr, sendData2, sizeof(sendData2));
usleep(1000);
std::cout << "\ninit lps22hb \n";
unsigned char readBuf0[] = {0};
readResister = 0x0f | 0x80;
read_block_data(lps22hbAdr, readResister, readBuf0, sizeof(readBuf0) );
std::cout << "WHO AM I (0xb1): 0x" << std::hex << (int)readBuf0[0]
<< "\n";
return 0;
}
int read_block_data(uint8_t address, unsigned char regi, unsigned char * readBuf, uint8_t byteCount ){
struct i2c_msg msg[2];
struct i2c_rdwr_ioctl_data packets;
unsigned char data[byteCount];
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = 1;
msg[0].buf = ®i;
std::cout << (int)regi << "\n";
msg[1].addr = address; // device address
msg[1].flags = I2C_M_RD; // アドレスを書き込み
msg[1].len = byteCount;
msg[1].buf = &data[0];
packets.msgs = msg;
packets.nmsgs = 2; // データ読み出しでmsgは2
int ret = ioctl(fd, I2C_RDWR, &packets);
std::cout << "all READ data : 0x";
for (int i = 0; i <byteCount; i++){
readBuf[i] = data[i];
std::cout << std::hex << (int)data[i] << " ";
}
std::cout << "\n";
return 0;
}
int read_block_data_noResister(uint8_t address, unsigned char * readBuf, uint8_t byteCount ){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
unsigned char data[byteCount];
msg[0].addr = address; // device address
msg[0].flags = I2C_M_RD; // アドレスを書き込み
msg[0].len = byteCount;
msg[0].buf = &data[0];
packets.msgs = msg;
packets.nmsgs = 1; // データ読み出しでmsgは1
int ret = ioctl(fd, I2C_RDWR, &packets);
std::cout << "all READ data : 0x" << std::hex << (int)data[0] << " , 0x"
<< std::hex << (int)data[1] << " , 0x" << std::hex << (int)data[2] << " , 0x"
<< std::hex << (int)data[3] << " , 0x" << std::hex << (int)data[4] << " , 0x"
<< std::hex << (int)data[5] << "\n";
for (int i = 0; i <byteCount; i++){
readBuf[i] = data[i];
}
return 0;
}
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
std::cout << "\nsend data is 0x" << std::hex << (int)address << "\t";
for ( int i=0; i < byteCount; i++){
std::cout << i << ": 0x" << std::hex << (int)sendData[i] << ", ";
}
std::cout << "\n";
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = byteCount;
msg[0].buf = sendData;
packets.msgs = msg;
packets.nmsgs = 1; // データ書き込みでmsgは1
int ret = ioctl(fd, I2C_RDWR, &packets);
return 0;
}
実行します。
$ g++ ex137.cpp
$ ./a.out
start lps22hb
send data is 0x71 0: 0x94,
send data is 0x71 0: 0x89,
init sht45
all READ data : 0x20 , 0x5c , 0xd7 , 0xe1 , 0xff , 0xff
all data : 0x20 , 0x5c , 0xd7 , 0xe1 , 0xff , 0xff
send data is 0x5d 0: 0x10, 1: 0x10,
init lps22hb
8f
all READ data : 0xb1
WHO AM I (0xb1): 0xb1
正しく1バイトが読み出せています。気圧も読み出します。
#include <iostream>
#include <string>
#include <iomanip>
#include <cstdint>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#define white_ht16k33Adr 0x71
#define red_ht16k33Adr 0x70
#define HT16K33_GENERIC_SYSTEM_ON 0x21
#define HT16K33_GENERIC_DISPLAY_ON 0x81
#define HT16K33_GENERIC_CMD_BRIGHTNESS 0xe0
#define sht45Adr 0x44
#define lps22hbAdr 0x5d
int fd;
int read_block_data(uint8_t address, unsigned char regi, unsigned char * readBuf, uint8_t byteCount );
int read_block_data_noResister(uint8_t address, unsigned char * readBuf, uint8_t byteCount );
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount);
int main(){
std::cout << "start lps22hb\n";
fd = open("/dev/i2c-1", O_RDWR);
uint8_t data0 = 0x94; // soft reset
uint8_t sendData0[] = {data0};
write_block_data(white_ht16k33Adr, sendData0, sizeof(sendData0));
usleep(1000);
uint8_t data1 = 0x89; // read serial number
uint8_t sendData1[] = {data1};
write_block_data(white_ht16k33Adr, sendData1, sizeof(sendData1));
usleep(10000);
std::cout << "\ninit sht45 \n";
unsigned char readBuf1[] = {0,0,0,1,2,3};
read_block_data_noResister(sht45Adr, readBuf1, sizeof(readBuf1));
std::cout << "all data : 0x" << std::hex << (int)readBuf1[0] << " , 0x"
<< std::hex << (int)readBuf1[1] << " , 0x" << std::hex << (int)readBuf1[2] << " , 0x"
<< std::hex << (int)readBuf1[3] << " , 0x" << std::hex << (int)readBuf1[4] << " , 0x"
<< std::hex << (int)readBuf1[5] << "\n";
unsigned char readResister = 0x10; // config resister
uint8_t data = 0x10; // wake up,5Hz
uint8_t sendData2[] = {readResister, data};
write_block_data(lps22hbAdr, sendData2, sizeof(sendData2));
usleep(1000);
std::cout << "\ninit lps22hb \n";
unsigned char readBuf0[] = {0};
readResister = 0x0f | 0x80;
read_block_data(lps22hbAdr, readResister, readBuf0, sizeof(readBuf0) );
std::cout << "WHO AM I (0xb1): 0x" << std::hex << (int)readBuf0[0]
<< "\n";
unsigned char readBuf01[] = {0,0,0,0,0};
readResister = 0x28 | 0x80 ;
read_block_data(lps22hbAdr, readResister, readBuf01, sizeof(readBuf01) );
std::cout << "all data: 0x" << std::hex << (int)readBuf01[0] << " 0x"
<< std::hex << (int)readBuf01[1]<< " 0x" << std::hex << (int)readBuf01[2] << " 0x"
<< std::hex << (int)readBuf01[3]<< " 0x" << std::hex << (int)readBuf01[4]
<< "\n";
float press = std::stof( std::to_string((readBuf01[2] << 16) | (readBuf01[1] <<8) | readBuf01[0] )) / 4096;
int tempNative = (readBuf01[4] << 8) | readBuf01[3];
int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
float temp = std::stof( std::to_string(temp2scomp)) / 100;
std::cout << "Press is " << press << "hPa , " << "Temp is " << temp << "`C\n";
return 0;
}
int read_block_data(uint8_t address, unsigned char regi, unsigned char * readBuf, uint8_t byteCount ){
struct i2c_msg msg[2];
struct i2c_rdwr_ioctl_data packets;
unsigned char data[byteCount];
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = 1;
msg[0].buf = ®i;
std::cout << (int)regi << "\n";
msg[1].addr = address; // device address
msg[1].flags = I2C_M_RD; // アドレスを書き込み
msg[1].len = byteCount;
msg[1].buf = &data[0];
packets.msgs = msg;
packets.nmsgs = 2; // データ読み出しでmsgは2
int ret = ioctl(fd, I2C_RDWR, &packets);
std::cout << "all READ data : 0x";
for (int i = 0; i <byteCount; i++){
readBuf[i] = data[i];
std::cout << std::hex << (int)data[i] << " ";
}
std::cout << "\n";
return 0;
}
int read_block_data_noResister(uint8_t address, unsigned char * readBuf, uint8_t byteCount ){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
unsigned char data[byteCount];
msg[0].addr = address; // device address
msg[0].flags = I2C_M_RD; // アドレスを書き込み
msg[0].len = byteCount;
msg[0].buf = &data[0];
packets.msgs = msg;
packets.nmsgs = 1; // データ読み出しでmsgは1
int ret = ioctl(fd, I2C_RDWR, &packets);
std::cout << "all READ data : 0x" << std::hex << (int)data[0] << " , 0x"
<< std::hex << (int)data[1] << " , 0x" << std::hex << (int)data[2] << " , 0x"
<< std::hex << (int)data[3] << " , 0x" << std::hex << (int)data[4] << " , 0x"
<< std::hex << (int)data[5] << "\n";
for (int i = 0; i <byteCount; i++){
readBuf[i] = data[i];
}
return 0;
}
int write_block_data(uint8_t address, uint8_t * sendData, uint8_t byteCount){
struct i2c_msg msg[1];
struct i2c_rdwr_ioctl_data packets;
std::cout << "\nsend data is 0x" << std::hex << (int)address << "\t";
for ( int i=0; i < byteCount; i++){
std::cout << i << ": 0x" << std::hex << (int)sendData[i] << ", ";
}
std::cout << "\n";
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = byteCount;
msg[0].buf = sendData;
packets.msgs = msg;
packets.nmsgs = 1; // データ書き込みでmsgは1
int ret = ioctl(fd, I2C_RDWR, &packets);
return 0;
}
実行します。
$ g++ ex138.cpp
$ ./a.out
start lps22hb
send data is 0x71 0: 0x94,
send data is 0x71 0: 0x89,
init sht45
all READ data : 0x10 , 0x69 , 0x17 , 0xe8 , 0xff , 0xff
all data : 0x10 , 0x69 , 0x17 , 0xe8 , 0xff , 0xff
send data is 0x5d 0: 0x10, 1: 0x10,
init lps22hb
8f
all READ data : 0xb1
WHO AM I (0xb1): 0xb1
a8
all READ data : 0xe 13 3f 9a a
all data: 0xe 0x13 0x3f 0x9a 0xa
Press is 1009.19hPa , Temp is 27.14`C
読み出した5バイト・データの並びがおかしいです。指でセンサをつまんで、変化する数値を見て、どのデータが、温度か、気圧かを判断しました。
以上で、i2cアクセスの関数化ができました。整理したのを次回に。
と思ったのですが、
int read_block_data(uint8_t address, unsigned char regi, unsigned char * readBuf, uint8_t byteCount ){
これでは、読み出しレジスタが2バイトのときの対応ができていないことに気が付きました。
#include <iostream>
#include <string>
#include <iomanip>
#include <cstdint>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#define deviceAdr 0x5d
int fd, data;
int read_block_data(uint8_t address, unsigned char * resi, uint8_t length, unsigned char * readBuf, uint8_t byteCount );
int main(){
fd = open("/dev/i2c-1", O_RDWR);
unsigned char readBuf[1] = {0};
unsigned char readResister[1] = {0x0f | 0x80} ;
read_block_data(deviceAdr, readResister, sizeof(readResister), readBuf, sizeof(readBuf) );
std::cout << "read data(who am I) 0xb1: 0x" << std::hex << (int)readBuf[0]
<< "\n";
return 0;
}
int read_block_data(uint8_t address, unsigned char * resi, uint8_t length, unsigned char * readBuf, uint8_t byteCount ){
struct i2c_msg msg[2];
struct i2c_rdwr_ioctl_data packets;
unsigned char resiData[length], data[byteCount];
msg[0].addr = address; // device address
msg[0].flags = 0; // write
msg[0].len = length;
msg[0].buf = &resiData[0];
if (length == 2) {
resiData[1] = resi[1];
//std::cout << (int)resi[1] <<"\n";
}
resiData[0] = resi[0];
// std::cout << (int)resi[0] <<"\n";
msg[1].addr = address; // device address
msg[1].flags = I2C_M_RD; // read
msg[1].len = byteCount;
msg[1].buf = &data[0];
packets.msgs = msg;
packets.nmsgs = 2;
int ret = ioctl(fd, I2C_RDWR, &packets);
for (int i = 0; i < byteCount; i++){
readBuf[i] = data[i];
}
return 0;
}
実行します。lps22hbなので、読み出すレジスタは1バイトです。プログラムは、1もしくは2バイトの時だけに対応しました。
$ g++ ex205.cpp
$ ./a.out
read data(who am I) 0xb1: 0xb1