LPC1114 I2C
1 DS7505 温度センサー
2 I2Cに思うこと
3 EEPRMのテスト(M24C64)
4 RTC DS3232
5 BMP180 気圧センサー
1 DS7505 温度センサー 2016.03.20
I2C通信の例としてMAXIMの温度センサーDS7505を取り上げることにしました。書き込みが少なく、温度のデジタル値を読み取るだけの作業です。
MCには キャラクタLCDとDS7505を接続します。ピン配置は次のとおりです。
LCD:rs-dp1 e-dp2 d4-dp17 d5-18 d6-25 d7-26
DS7505:sck-27(10k pullup) sda-5(10k pullup) Vdd-3.3V+ GND-GND
DS7505の主な仕様です。
・アドレスは、0x48(7ビット)、0x90(8ビットW)0x91(8ビットR)です。
・温度データは2バイトで、MSBが整数部分(摂氏温度の)、LSBの上位4ビットが 1/2、1/4、1/8、1/16℃を示します。LSBの下位4ビットは0です。
・内部に設定レジスタがあって、0が書き込まれていれば温度読み取りレジスタが選択され、1であれば設定レジスタを書き換えるモードになります。eepromで残るのでレジスタを設定したあとは0に戻しておかないと温度が読み取れません。
・設定レジスタに 0x60 を書き込むと温度は12バイト読み取りになります。(変換に0.2secかかります)
・設定レジスタにはサーモスタットの仕様がありますが使わないので省略します。
・i2cの手続きは次のようです。
温度2バイト読み取り 温度12ビットに設定 温度読み取りモードに戻す
STARTコンディション STARTコンディション STARTコンディション
アドレス0x91 アドレス0x90 アドレス0x90
DATA(MSB) 1を書き込み 0を書き込み
DATA(LSB) 0x60を書き込み STOPコンディション
STOPコンディション STOPコンディション
プログラムは以下のようになりました。(ゴミも多いw)
/* *************************************
temp01.cpp
***************************************** */
#include "mbed.h"
#include "TextLCD.h"
TextLCD lcd(dp1, dp2, dp17, dp18, dp25, dp26);
// rs e d4 d5 d6 d7
I2C i2c(dp5, dp27); // sda, scl
Serial pc(USBTX, USBRX); // tx, rx
const int addr = 0x90; // define the I2C Address 8bits
int main(void) {
char cmd[2];
/* set 12bites data ***************/
cmd[0]=1; cmd[1]=0x60;
i2c.write(addr,cmd,2); wait(0.1); // 12bit (addrに cmd[]を 2バイト書く)
cmd[0]=0;
i2c.write(addr,cmd,1); wait(0.1); // to read temperaure
/* end of set 12bites data ***************/
while(1){
i2c.read(addr | 1, cmd, 2); // read the two-byte echo result (addr+1から 2バイトを cmd[]に読む)
float echo = cmd[0]+(cmd[1]/256.0);
lcd.cls();
lcd.writeCommand(12); wait(0.1); //cursor off
lcd.locate(1, 0);
lcd.printf("%.2f c\n", echo);
wait(2);
}
}
リセットをかけるとなぜか表示が乱れることがあります。しかし、2,3回書き換えられると収まります。
printf()があるので表示は楽です。
I2C通信が簡単に書けます。
2 I2Cに思うこと 2016.03.23
今までの経験から、I2Cの通信には3つのタイプがあります。
第一は、内部アドレスを持たない型です。(あるいは内部アドレスがあっても書き込みだけの場合です。)
これには温度センサーDS7505が該当します。
標準のI2Cの name.write()、name.read() が使えます。連続の(多バイトの)W/R可能。
I2C name( sda, scl); // ex l2C i2c(dp5, dp27);
name.write(); // i2c.write(addr,cmd,2); ( char cmd[n]; )
name.read(); // i2c.read(addr | 1, cmd, 2);
第二は、内部アドレスが1バイトのものです。RTC DS3231 が該当します。
第三は、内部アドレスが2バイトのもので、読み取り時には in_addr を2バイト書いて、後に読み取りを行う必要があります。EEPROMがこれに当てはまります。
書き込み時は、内部アドレス1バイトの時は書き込みの第1バイトに内部アドレス、第2バイトにデータ1を書き、内部アドレス2バイトの時は、addrH,addrL,data1,…を書き込みます。
いずれも連続書き込みの name.write(addr,cmd,n); ただし cmd[]はchar配列、nは整数、で書き込めます。(eepromの書き込みwaitは考えていません)
読み取り時は start c addr(W) in_addrH in_addrL start c addr(R) data stop c (1バイト読み取り)
この時、書き込みルーチンで内部アドレスを書き込み、stopコンディションなしに読み込みルーチンを実行することになります。その点でmbedのI2Cにはstopコンディションなしに書き込みルーチンを実行し、続いて読み取りルーチンを実行する方法が考えられています。ここにその方法が書かれています。下の方の Information です。
例えば、デバイスアドレス0xA0(8bit)、内部アドレスH 0x10 内部アドレスL 0x23 のデータを1バイト読みだすには、nameをi2cとして、
cmd[0]=0x10; cmd[1]=0x23;
i2c.write(0xA0,cmd,2,true); i2c.read(0xA0|1,&val,1);
と、太字の true を書いておけばstopコンディションなしの連続した動作となります。
なお、read時のアドレスは +1 になるはずですが、write時(LSB=0)と同じアドレスを書いても正常に実行されます。
eepromの書き込みでは約5msの待ち時間が必要ですから、書き込み時は1バイト書き込みでwait()を置き、ループで実行するのが良いと思います。(待ち時間をwrite()が待ってくれるのかどうかは未確認です)
標準のルーチンでは、
start c → name.start();
stop c → name.stop();
control b → w:addr, r:addr+1
read → val = name.read(ack); val = name.read(nack); val: 受信データ
write → val = name.write(data); val: 1=ack受信、0=他
連続送信
val = name.write(address, data, length, repeated);
address: I2Cアドレス(1〜7bit)
data: 送信データの配列(char *)
length: 送信データのバイト数
repeated: false=ストップコンディション出力、true=ストップコンディション省略、リスタート用(省略時=false)
val: 結果 0=成功、0以外=失敗
|
3 EEPRMのテスト(M24C64) 2016.03.23
I2C接続のeepromを試してみました。デバイスは M24C64-RDW6TPというもので秋月で5個\100のものです。安くて小さいのは良いのですが、小さすぎてハンダ付けに苦労します。
mbedで便利なプログラムがアップされていないかと探しましたが私にわかるようなプログラムは見つかりませんでした。
内部アドレスが2バイトあるので書き込みはともかく、読み取りはどうしようかと悩み(?)ましたが、標準で用意されているI2Cライブラリに便利な機能が用意されている記述を発見しました。
読み取り時には、書き込みWアドレスで内部アドレスを書き込み、stopコンディションをつけないでリスタートコンディション(startコンディションと同じ)で読み取りR手続きに入るわけですが、mbedのI2Cでは連続書き込みの i2c.write(addr,&val,2,true); この文の true を書いておくと stop c が付かなくなりリスタートにつなげるようです。
なお、eepromは書き込みに5msほどの時間がかかりますので、書き込みは連続で行わず、1文字ずつループで書き込んでいます。
プログラムは任意の番地から8バイトデータを書き込み、任意の番地から8バイトを読み出せるようにしました。
慣れない環境でずいぶんなロスもありましたが、時間を重ねて初期の目標に達することができました。
シリアルが1回路しかないので、プログラマとTeraTermに切り替えながらのテストでした。
// eeprom01 pc->eeprom, eeprom->pc eeprom_test
// uart 9600bps 8N1
#include "mbed.h"
Serial pc(USBTX, USBRX);
I2C i2c(dp5, dp27); // sda, scl
//const int addr = 0xA0; // define the I2C Address 8bits
int main() {
char rw, s[20], d[10];
uint8_t i=0;
uint16_t inAdd;
while(1){
pc.printf("EEPROM write=1 read=2 1 or 2 ?");
rw=pc.getc();
pc.printf("\n\raddress(0000--8000)? ");
for(i=0;i<4;i++){s[i] = pc.getc(); }
s[4]=0;
inAdd=(s[0]-'0')*1000+(s[1]-'0')*100+(s[2]-'0')*10+(s[3]-'0');
pc.printf("\n\r");
if(rw=='1'){//write
pc.printf("%s\n\r Input data 8bit --",s);
for(i=0;i<8;i++){d[i] = pc.getc();} d[8]=0;
pc.printf("\n\r");
for(int i=0;i<8;i++){
s[0]=inAdd/256; s[1]=inAdd%256;
s[2]=d[i]; i2c.write(0xa0,s,3); wait_ms(6);
inAdd++;
}
}else{//read
s[0]=inAdd/256; s[1]=inAdd%256;
i2c.write(0xa0|1,s,2,true); i2c.read(0xa0,d,8);
d[8]=0;
pc.printf("data= %s\n\r",d);
}
pc.printf(" \n\r");
}
}
4 RTC DS3232 2016.03.26
時刻保持用のリチウム電池とI2C用の端子をつけたDS3232がありましたので接続してみました。今回はI2Cの書き込みと読み出しのテストだけですので、細かな設定はぜず、時刻は 時、分、秒 の書き込みと読み出しだけにしました。
時刻設定はTeraTermで行い、時刻表示はLCD(3.3V用)を使っています。
RESETボタンを押すとTeraTermが繋がっていないと動作を始めませんが、I2Cテスト用ということで辛抱します。1秒の表示切り替えも本来なら秒パルスで行うのですが、簡単のため0.1秒ウエイトで表示を繰り返しています。
プログラムです。
// rtc01.cpp DS3232
#include "mbed.h"
#include "TextLCD.h"
TextLCD lcd(dp1, dp2, dp17, dp18, dp25, dp26); // rs e d4 d5 d6 d7
I2C i2c(dp5, dp27); // sda, scl
Serial pc(USBTX, USBRX); // tx, rx
//const int addr = 0x90; // define the I2C Address 8bits temperature sensor
const int addr1 = 0xd0; // define the I2C Address 8bits RTC DS3232
int main(void) {
char menu,i,cmd[2];
char s[20],d[10];
lcd.cls();wait(1.0);
start:
// ------- menu -----------------
pc.printf("--- MENU ---\n\r");
pc.printf(" 1 Clock on LCD\n\r");
pc.printf(" 2 Set Time\n\r");
pc.printf(" 1 or 2 ? ");
menu=pc.getc();
pc.printf("\n\r");
while(1){//--1
if(menu=='1'){ // menu 1 時刻表示 1111111
pc.printf(" Clock on LCD\n\r");
while(1){//--2
s[0]=0; s[1]=0;
i2c.write(addr1,s,1,true);
i2c.read(addr1|1,d,3);
lcd.locate(0, 0);
lcd.writeCommand(12); wait(0.1); //cursor off
lcd.locate(0, 1);
lcd.printf(" ");
lcd.locate(0, 0);
lcd.printf(" TIME %d%d:",d[2]/16,d[2]%16);//hour
lcd.printf("%d%d:",d[1]/16,d[1]%16); //min
lcd.printf("%d%d" ,d[0]/16,d[0]%16); //sec
wait(0.1);
}// end of while 2
}//end of if--1
if(menu!='1'){ // menu 2 時刻合わせ 22222
pc.printf("Input hhmmss on 24H\n\r");wait(0.5);
for(i=0;i<6;i++){
s[i]=pc.getc();
}
d[3]=(s[0]-'0')*16+(s[1]-'0'); // hh
d[2]=(s[2]-'0')*16+(s[3]-'0'); // mm
d[1]=(s[4]-'0')*16+(s[5]-'0'); //ss
d[3]=d[3]&0B10111111; // set 24h
d[0]=0;
//i2c.write(addr1);
i2c.write(addr1,d,4);
goto start;
}//end of if menu=2
}
}
TeraTermは 9600bps 8N1 です。なお、mbedでは2バイト文字は SJIS でなく UFT-8 になっています。
pc.printf()、lcd.printf()が使えるので表示は楽です。
一箇所、よくわからないのがRTCの書き込みも読み出しもインナーアドレスに該当する 0x00 値の1バイトを先頭のアドレスバイトに続けて書き込まないと正しい秒分時が得られません。同じデバイスをR8C/M12Aの自作プログラムで読み書きしたときは必要ありませんでした。mbedでは、
読み出し時: s[0]=0;
i2c.write(addr1,s,1,true); // true: no stop condition
i2c.read(addr1|1,d,3); // d[ ]
書き込み時: d[0]=0;
i2c.write(addr1,d,4); // d[1]--d[4] data
となりました。
5 BMP180 気圧センサー 2016.03.26
圧力センサーBMP180を繋いでみました。このセンサーはR8C/M12A用の自作I2Cルーチンでは読み書きができなかったもので、Arduinoではライブラリを使って簡単に書くことができたものです。
mbed用のライブラリを探したところ、このページにライブラリが、また別のページにはmain.cppがありましたので実行してみました。
このICは多くのデータを読みだして、かなり複雑な計算が必要なものですが、ライブラリが計算してくれて計算結果だけを返してくれます。なお、第1代の気圧計と数値を合わせるために+4.4hPaの補正をしています。
#include "mbed.h"
#include "BMP180.h"
#include "TextLCD.h"
TextLCD lcd(dp1, dp2, dp17, dp18, dp25, dp26); // rs e d4 d5 d6 d7
Serial pc(dp16,dp15); // UART (vertual COM)
BMP180 bmp180(dp5,dp27); // Bosch sensor
//-------------------------------------------------------------------------------------------------
// Control Program
//-------------------------------------------------------------------------------------------------
int main() {
while(1) {
// ---------- Barometer Sensor / BMP180 --------------------------------------------------
bmp180.normalize();
lcd.writeCommand(12); wait(0.1); //cursor off
lcd.locate(0,0);
lcd.printf("%4.2f %0.2f",
bmp180.read_pressure()+4.4, bmp180.read_temperature());
wait(1.0);
}
}
今までものを総合するとリアルタイムクロック付き、ロガー付きの気圧計ができるのですが、32kBに収まるのでしょうか。
工事中
4 TeraTermと通信 2016.03.18
工事中