秋月で、「LPS331使用 高精度大気圧モジュール」を650円で入手しましたので、そのテストです。大気圧モジュールの温度補償用として精度は±2 degCほどのようですが、温度計もついています。通信プロトコルとして、I2CとSPIに対応しています。5番ピンをプルアップするかしないかで、I2C/SPIの切り替えができます。AVRを使って、I2Cの通信の練習をしようと思いましたので、I2Cモードで通信のテストをしてみます。AVRにはTWIにハードウェア対応しているatmega328Pを使います。
LPS331APは3.3V動作です。まず、この前作ったUSB-AVRライタは5V供給ですので、3.3 Vレギュレータを入れて、3.3 Vにもジャンパピンで対応できるようにさくりと工作しました。
そして、適当にコーディングをしてから、PC5(SCL), PC4(SDA)をLPS331APへ配線します。WHO_AM_Iを連打するコードをつくり、ロジック・アナライザで動作チェックをしました。
一発目から無事、通信できていました。
ロジック・アナライザのカラフルな感じが好きです。
コードはこちら。SCL, SDAを外部でプルアップしない場合は、PC5, PC4についてPORTCレジスタに1を入れてやれば、AVR内部でプルアップしてもらえます。ただAVRのプルアップは弱いので(抵抗が大きいので)100kbps以上の早い通信をしようとすると失敗しました。エラー処理はしていません。
#include <avr/io.h>
#include <util/delay.h>
#include <util/twi.h>
#define SLA_R 0b10111001
#define SLA_W 0b10111000
#define SCL PORTC5
#define SDA PORTC4
int8_t read_one(uint8_t reg_add);
int main(void){
PORTC = (1 << SCL) | (1 << SDA);//pull up
// 1e6/(16+2*TWBR*TWPS) < 100 kHz = 1e5
//TWSR |= (0 << TWPS1) | (0 << TWPS0); //prescaler 1
//TWBR = 0x00;// TWBR = 0
//TWAR = //address & general call
while(1){
read_one(0x0f);
_delay_ms(100);
}
}
int8_t read_one(uint8_t reg_add){
int8_t res;
// ST send start bit
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // | (1 << TWIE);
while (!(TWCR & (1 << TWINT))); // wait
if((TWSR & 0xf8) != TW_START){ // chk status
return -1; //error
}
// SLA+W
TWDR = SLA_W;
TWCR = (1 << TWINT) | (1 << TWEN);//return
while (!(TWCR & (1 << TWINT)));//wait
if((TWSR & 0xf8) != TW_MT_SLA_ACK){ //chk
return -1;//error
}
// SUB: multiread? + register add
TWDR = reg_add + (0 << 7); //single read MSB = 0;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));//wait
if((TWSR & 0xf8) != TW_MT_DATA_ACK){ //chk
return -1;//error
}
// SR
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // | (1 << TWIE);
while (!(TWCR & (1 << TWINT))); // wait
if((TWSR & 0xf8) != TW_REP_START){ // chk status
return -1; //error
}
// SLA + R
TWDR = SLA_R;
TWCR = (1 << TWINT) | (1 << TWEN);//return
while (!(TWCR & (1 << TWINT)));//wait
if((TWSR & 0xf8) != TW_MR_SLA_ACK){ //chk
return -1;//error
}
// DATA read
TWCR = (1 << TWINT) | (1 << TWEN) | (0 << TWEA); //NACK
while (!(TWCR & (1 << TWINT)));//wait
if((TWSR & 0xf8) != TW_MR_DATA_NACK){ //chk
return -1;//error
}
res = TWDR;
// SP
TWCR = (1 << TWINT) | (1 << TWSTO)| (1 << TWEN);
return res;
}